1 /*
2  * xmpp_iq.c    -- Jabber protocol IQ-related stuff
3  *
4  * Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de>
5  * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
6  * Parts come from the centericq project:
7  * Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua>
8  * Some small parts come from the Pidgin project <http://pidgin.im/>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or (at
13  * your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include <string.h>
25 #include <sys/utsname.h>
26 
27 #include "xmpp_helper.h"
28 #include "commands.h"
29 #include "screen.h"
30 #include "utils.h"
31 #include "logprint.h"
32 #include "settings.h"
33 #include "caps.h"
34 #include "main.h"
35 
36 extern struct xmpp_error xmpp_errors[];
37 
38 static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
39                                                     LmConnection *c,
40                                                     LmMessage *m,
41                                                     gpointer ud);
42 
43 static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
44                                                           LmConnection *c,
45                                                           LmMessage *m,
46                                                           gpointer ud);
47 
48 inline double seconds_since_last_use(void);
49 
50 struct adhoc_command {
51   char *name;
52   char *description;
53   bool only_for_self;
54   LmHandleMessageFunction callback;
55 };
56 
57 const struct adhoc_command adhoc_command_list[] = {
58   { "http://jabber.org/protocol/rc#set-status",
59     "Change client status",
60     1,
61     &handle_iq_command_set_status },
62   { "http://jabber.org/protocol/rc#leave-groupchats",
63     "Leave groupchat(s)",
64     1,
65     &handle_iq_command_leave_groupchats },
66   { NULL, NULL, 0, NULL },
67 };
68 
69 struct adhoc_status {
70   char *name;   // the name used by adhoc
71   char *description;
72   char *status; // the string, used by setstus
73 };
74 // It has to match imstatus of roster.h!
75 const struct adhoc_status adhoc_status_list[] = {
76   {"offline", "Offline", "offline"},
77   {"online", "Online", "avail"},
78   {"chat", "Chat", "free"},
79   {"dnd", "Do not disturb", "dnd"},
80   {"xa", "Extended away", "notavail"},
81   {"away", "Away", "away"},
82 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
83   {"invisible", "Invisible", "invisible"},
84 #endif
85   {NULL, NULL, NULL},
86 };
87 
generate_session_id(char * prefix)88 static char *generate_session_id(char *prefix)
89 {
90   char *result;
91   static int counter = 0;
92   counter++;
93   // TODO better use timestamp?
94   result = g_strdup_printf("%s-%i", prefix, counter);
95   return result;
96 }
97 
lm_message_new_iq_error(LmMessage * m,guint error)98 static LmMessage *lm_message_new_iq_error(LmMessage *m, guint error)
99 {
100   LmMessage *r;
101   LmMessageNode *err;
102   int i;
103 
104   if (G_UNLIKELY(!m)) return NULL;
105 
106   for (i = 0; xmpp_errors[i].code; ++i)
107     if (xmpp_errors[i].code == error)
108       break;
109   g_return_val_if_fail(xmpp_errors[i].code > 0, NULL);
110 
111   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_ERROR);
112   err = lm_message_node_add_child(r->node, "error", NULL);
113   lm_message_node_set_attribute(err, "code", xmpp_errors[i].code_str);
114   lm_message_node_set_attribute(err, "type", xmpp_errors[i].type);
115   lm_message_node_set_attribute
116           (lm_message_node_add_child(err,
117                                      xmpp_errors[i].condition, NULL),
118            "xmlns", NS_XMPP_STANZAS);
119 
120   return r;
121 }
122 
send_iq_error(LmConnection * c,LmMessage * m,guint error)123 void send_iq_error(LmConnection *c, LmMessage *m, guint error)
124 {
125   LmMessage *r;
126   r = lm_message_new_iq_error(m, error);
127   if (r) {
128     lm_connection_send(c, r, NULL);
129     lm_message_unref(r);
130   }
131 }
132 
lm_message_node_add_dataform_result(LmMessageNode * node,const char * message)133 static void lm_message_node_add_dataform_result(LmMessageNode *node,
134                                                 const char *message)
135 {
136   LmMessageNode *x, *field;
137 
138   x = lm_message_node_add_child(node, "x", NULL);
139   lm_message_node_set_attributes(x,
140                                  "type", "result",
141                                  "xmlns", "jabber:x:data",
142                                  NULL);
143   field = lm_message_node_add_child(x, "field", NULL);
144   lm_message_node_set_attributes(field,
145                                  "type", "text-single",
146                                  "var", "message",
147                                  NULL);
148   lm_message_node_add_child(field, "value", message);
149 }
150 
151 // Dummy handler to ignore IQ response
handle_iq_dummy(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)152 LmHandlerResult handle_iq_dummy(LmMessageHandler *h, LmConnection *c,
153                                  LmMessage *m, gpointer ud)
154 {
155   LmMessageSubType mstype = lm_message_get_sub_type(m);
156   if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) {
157     display_server_error(lm_message_node_get_child(m->node, "error"),
158                          lm_message_get_from(m));
159   }
160   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
161 }
162 
handle_iq_commands_list(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)163 static LmHandlerResult handle_iq_commands_list(LmMessageHandler *h,
164                                                LmConnection *c,
165                                                LmMessage *m, gpointer ud)
166 {
167   LmMessage *iq;
168   LmMessageNode *query;
169   const char *requester_jid;
170   const struct adhoc_command *command;
171   const char *node;
172   gboolean from_self;
173 
174   iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
175   query = lm_message_node_add_child(iq->node, "query", NULL);
176   lm_message_node_set_attribute(query, "xmlns", NS_COMMANDS);
177   node = lm_message_node_get_attribute
178           (lm_message_node_get_child(m->node, "query"),
179            "node");
180   if (node)
181     lm_message_node_set_attribute(query, "node", node);
182 
183   requester_jid = lm_message_get_from(m);
184   from_self = jid_equal(lm_connection_get_jid(c), requester_jid);
185 
186   for (command = adhoc_command_list ; command->name ; command++) {
187     if (!command->only_for_self || from_self) {
188       lm_message_node_set_attributes
189               (lm_message_node_add_child(query, "item", NULL),
190                "node", command->name,
191                "name", command->description,
192                "jid", lm_connection_get_jid(c),
193                NULL);
194     }
195   }
196 
197   lm_connection_send(c, iq, NULL);
198   lm_message_unref(iq);
199   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
200 }
201 
handle_iq_command_set_status(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)202 static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h,
203                                                     LmConnection *c,
204                                                     LmMessage *m, gpointer ud)
205 {
206   const char *action, *node;
207   char *sessionid;
208   LmMessage *iq;
209   LmMessageNode *command, *x, *y;
210   const struct adhoc_status *s;
211 
212   x = lm_message_node_get_child(m->node, "command");
213   action = lm_message_node_get_attribute(x, "action");
214   node = lm_message_node_get_attribute(x, "node");
215   sessionid = (char *)lm_message_node_get_attribute(x, "sessionid");
216 
217   iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
218   command = lm_message_node_add_child(iq->node, "command", NULL);
219   lm_message_node_set_attribute(command, "node", node);
220   lm_message_node_set_attribute(command, "xmlns", NS_COMMANDS);
221 
222   if (!sessionid) {
223     sessionid = generate_session_id("set-status");
224     lm_message_node_set_attribute(command, "sessionid", sessionid);
225     g_free(sessionid);
226     sessionid = NULL;
227     lm_message_node_set_attribute(command, "status", "executing");
228 
229     x = lm_message_node_add_child(command, "x", NULL);
230     lm_message_node_set_attribute(x, "type", "form");
231     lm_message_node_set_attribute(x, "xmlns", "jabber:x:data");
232 
233     lm_message_node_add_child(x, "title", "Change Status");
234 
235     lm_message_node_add_child(x, "instructions",
236                               "Choose the status and status message");
237 
238     // TODO see if factorisation is possible
239     y = lm_message_node_add_child(x, "field", NULL);
240     lm_message_node_set_attribute(y, "type", "hidden");
241     lm_message_node_set_attribute(y, "var", "FORM_TYPE");
242 
243     lm_message_node_add_child(y, "value", "http://jabber.org/protocol/rc");
244 
245     y = lm_message_node_add_child(x, "field", NULL);
246     lm_message_node_set_attributes(y,
247                                    "type", "list-single",
248                                    "var", "status",
249                                    "label", "Status",
250                                    NULL);
251     lm_message_node_add_child(y, "required", NULL);
252 
253     // XXX: ugly
254     lm_message_node_add_child(y, "value",
255                               adhoc_status_list[xmpp_getstatus()].name);
256     for (s = adhoc_status_list; s->name; s++) {
257         LmMessageNode *option = lm_message_node_add_child(y, "option", NULL);
258         lm_message_node_add_child(option, "value", s->name);
259         lm_message_node_set_attribute(option, "label", s->description);
260     }
261     // TODO add priority ?
262     // I do not think this is useful, user should not have to care of the
263     // priority like gossip and gajim do (misc)
264     lm_message_node_set_attributes
265             (lm_message_node_add_child(x, "field", NULL),
266              "type", "text-multi",
267              "var", "status-message",
268              "label", "Message",
269              NULL);
270   } else if (action && !strcmp(action, "cancel")) {
271     lm_message_node_set_attribute(command, "status", "canceled");
272   } else  { // (if sessionid and not canceled)
273     y = lm_message_node_find_xmlns(x, "jabber:x:data"); //x?xmlns=jabber:x:data
274     if (y) {
275       const char *value=NULL, *message=NULL;
276       LmMessageNode *fields, *field;
277       field = fields = lm_message_node_get_child(y, "field"); //field?var=status
278       while (field && strcmp("status",
279                              lm_message_node_get_attribute(field, "var")))
280         field = field->next;
281       field = lm_message_node_get_child(field, "value");
282       if (field)
283         value = lm_message_node_get_value(field);
284       field = fields; //field?var=status-message
285       while (field && strcmp("status-message",
286                              lm_message_node_get_attribute(field, "var")))
287         field = field->next;
288       field = lm_message_node_get_child(field, "value");
289       if (field)
290         message = lm_message_node_get_value(field);
291       if (value) {
292         for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
293         if (s->name) {
294           char *status = g_strdup_printf("%s %s", s->status,
295                                          message ? message : "");
296           cmd_setstatus(NULL, status);
297           g_free(status);
298           lm_message_node_set_attribute(command, "status", "completed");
299           lm_message_node_add_dataform_result(command,
300                                               "Status has been changed");
301         }
302       }
303     }
304   }
305   if (sessionid)
306     lm_message_node_set_attribute(command, "sessionid", sessionid);
307   lm_connection_send(c, iq, NULL);
308   lm_message_unref(iq);
309   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
310 }
311 
_callback_foreach_buddy_groupchat(gpointer rosterdata,void * param)312 static void _callback_foreach_buddy_groupchat(gpointer rosterdata, void *param)
313 {
314   LmMessageNode *field, *option;
315   const char *room_jid, *nickname;
316   char *desc;
317 
318   room_jid = buddy_getjid(rosterdata);
319   if (!room_jid) return;
320   nickname = buddy_getnickname(rosterdata);
321   if (!nickname) return;
322   field = param;
323 
324   option = lm_message_node_add_child(field, "option", NULL);
325   lm_message_node_add_child(option, "value", room_jid);
326   desc = g_strdup_printf("%s on %s", nickname, room_jid);
327   lm_message_node_set_attribute(option, "label", desc);
328   g_free(desc);
329 }
330 
handle_iq_command_leave_groupchats(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)331 static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h,
332                                                           LmConnection *c,
333                                                           LmMessage *m,
334                                                           gpointer ud)
335 {
336   const char *action, *node;
337   char *sessionid;
338   LmMessage *iq;
339   LmMessageNode *command, *x;
340 
341   x = lm_message_node_get_child(m->node, "command");
342   if (!x)
343     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
344 
345   action = lm_message_node_get_attribute(x, "action");
346   node = lm_message_node_get_attribute(x, "node");
347   sessionid = (char*)lm_message_node_get_attribute(x, "sessionid");
348 
349   iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
350   command = lm_message_node_add_child(iq->node, "command", NULL);
351   lm_message_node_set_attributes(command,
352                                  "node", node,
353                                  "xmlns", NS_COMMANDS,
354                                  NULL);
355 
356   if (!sessionid) {
357     LmMessageNode *field;
358 
359     sessionid = generate_session_id("leave-groupchats");
360     lm_message_node_set_attribute(command, "sessionid", sessionid);
361     g_free(sessionid);
362     sessionid = NULL;
363     lm_message_node_set_attribute(command, "status", "executing");
364 
365     x = lm_message_node_add_child(command, "x", NULL);
366     lm_message_node_set_attributes(x,
367                                    "type", "form",
368                                    "xmlns", "jabber:x:data",
369                                    NULL);
370 
371     lm_message_node_add_child(x, "title", "Leave groupchat(s)");
372 
373     lm_message_node_add_child(x, "instructions",
374                               "What groupchats do you want to leave?");
375 
376     field = lm_message_node_add_child(x, "field", NULL);
377     lm_message_node_set_attributes(field,
378                                    "type", "hidden",
379                                    "var", "FORM_TYPE",
380                                    NULL);
381 
382     lm_message_node_add_child(field, "value",
383                               "http://jabber.org/protocol/rc");
384 
385     field = lm_message_node_add_child(x, "field", NULL);
386     lm_message_node_set_attributes(field,
387                                    "type", "list-multi",
388                                    "var", "groupchats",
389                                    "label", "Groupchats: ",
390                                    NULL);
391     lm_message_node_add_child(field, "required", NULL);
392 
393     foreach_buddy(ROSTER_TYPE_ROOM, &_callback_foreach_buddy_groupchat, field);
394     // TODO: return an error if we are not connected to groupchats
395   } else if (action && !strcmp(action, "cancel")) {
396     lm_message_node_set_attribute(command, "status", "canceled");
397   } else  { // (if sessionid and not canceled)
398     LmMessageNode *form = lm_message_node_find_xmlns(x, "jabber:x:data");// TODO
399     if (form) {
400       LmMessageNode *field;
401 
402       lm_message_node_set_attribute(command, "status", "completed");
403       // TODO: implement sth. like "field?var=groupchats" in xmlnode...
404       field  = lm_message_node_get_child(form, "field");
405       while (field && strcmp("groupchats",
406                              lm_message_node_get_attribute(field, "var")))
407         field = field->next;
408 
409       if (field)
410         for (x = field->children ; x ; x = x->next)
411         {
412           if (!strcmp (x->name, "value")) {
413             GList* b = buddy_search_jid(lm_message_node_get_value(x));
414             if (b)
415               cmd_room_leave(b->data, "Requested by remote command");
416           }
417         }
418       lm_message_node_add_dataform_result(command,
419                                           "Groupchats have been left");
420     }
421   }
422   if (sessionid)
423     lm_message_node_set_attribute(command, "sessionid", sessionid);
424   lm_connection_send(c, iq, NULL);
425   lm_message_unref(iq);
426   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
427 }
428 
handle_iq_commands(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)429 LmHandlerResult handle_iq_commands(LmMessageHandler *h,
430                                    LmConnection *c,
431                                    LmMessage *m, gpointer ud)
432 {
433   const char *requester_jid = NULL;
434   LmMessageNode *cmd;
435   const struct adhoc_command *command;
436 
437   // mcabber has only partial XEP-0146 support...
438   if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type(m))
439     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
440 
441   requester_jid = lm_message_get_from(m);
442 
443   cmd = lm_message_node_get_child(m->node, "command");
444   if (!cmd) {
445     //send_iq_error(c, m, XMPP_ERROR_BAD_REQUEST);
446     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
447   }
448   if (jid_equal(lm_connection_get_jid(c), requester_jid)) {
449     const char *action, *node;
450     action = lm_message_node_get_attribute(cmd, "action");
451     node = lm_message_node_get_attribute(cmd, "node");
452     // action can be NULL, in which case it seems to take the default,
453     // ie execute
454     if (!action || !strcmp(action, "execute") || !strcmp(action, "cancel")
455         || !strcmp(action, "next") || !strcmp(action, "complete")) {
456       for (command = adhoc_command_list; command->name; command++) {
457         if (!strcmp(node, command->name))
458           command->callback(h, c, m, ud);
459       }
460       // "prev" action will get there, as we do not implement it,
461       // and do not authorize it
462     } else {
463       LmMessage *r;
464       LmMessageNode *err;
465       r = lm_message_new_iq_error(m, XMPP_ERROR_BAD_REQUEST);
466       if (r) {
467         err = lm_message_node_get_child(r->node, "error");
468         lm_message_node_set_attribute
469           (lm_message_node_add_child(err, "malformed-action", NULL),
470            "xmlns", NS_COMMANDS);
471         lm_connection_send(c, r, NULL);
472         lm_message_unref(r);
473       }
474     }
475   } else {
476     send_iq_error(c, m, XMPP_ERROR_FORBIDDEN);
477   }
478   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
479 }
480 
481 
handle_iq_disco_items(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)482 LmHandlerResult handle_iq_disco_items(LmMessageHandler *h,
483                                       LmConnection *c,
484                                       LmMessage *m, gpointer ud)
485 {
486   LmMessageNode *query;
487   const char *node = NULL;
488   query = lm_message_node_get_child(m->node, "query");
489   if (query)
490     node = lm_message_node_get_attribute(query, "node");
491   if (node) {
492     if (!strcmp(node, NS_COMMANDS)) {
493       return handle_iq_commands_list(NULL, c, m, ud);
494     } else {
495       send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
496     }
497   } else {
498     // not sure about this one
499     send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED);
500   }
501   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
502 }
503 
504 
_disco_add_feature_helper(gpointer data,gpointer user_data)505 void _disco_add_feature_helper(gpointer data, gpointer user_data)
506 {
507   LmMessageNode *node = user_data;
508   lm_message_node_set_attribute
509           (lm_message_node_add_child(node, "feature", NULL), "var", data);
510 }
511 
512 //  disco_info_set_caps(ansquery, entitycaps)
513 // Add features attributes to ansquery.  entitycaps should either be a
514 // valid capabilities hash or NULL. If it is NULL, the node attribute won't
515 // be added to the query child and Entity Capabilities will be announced
516 // as a feature.
517 // Please change the entity version string if you modify mcabber disco
518 // source code, so that it doesn't conflict with the upstream client.
disco_info_set_caps(LmMessageNode * ansquery,const char * entitycaps)519 static void disco_info_set_caps(LmMessageNode *ansquery,
520                                 const char *entitycaps)
521 {
522   if (entitycaps) {
523     char *eversion;
524     eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entitycaps);
525     lm_message_node_set_attribute(ansquery, "node", eversion);
526     g_free(eversion);
527   }
528 
529   lm_message_node_set_attributes
530           (lm_message_node_add_child(ansquery, "identity", NULL),
531            "category", "client",
532            "name", PACKAGE_STRING,
533            "type", "pc",
534            NULL);
535 
536   if (entitycaps)
537     caps_foreach_feature(entitycaps, _disco_add_feature_helper, ansquery);
538   else
539     caps_foreach_feature(entity_version(xmpp_getstatus()),
540                          _disco_add_feature_helper,
541                          ansquery);
542 }
543 
handle_iq_disco_info(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)544 LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
545                                      LmConnection *c,
546                                      LmMessage *m, gpointer ud)
547 {
548   LmMessage *r;
549   LmMessageNode *query, *tmp;
550   const char *node = NULL;
551   const char *param = NULL;
552 
553   if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_RESULT)
554     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
555 
556   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
557   query = lm_message_node_add_child(r->node, "query", NULL);
558   lm_message_node_set_attribute(query, "xmlns", NS_DISCO_INFO);
559   tmp = lm_message_node_get_child(m->node, "query");
560   if (tmp) {
561     node = lm_message_node_get_attribute(tmp, "node");
562     param = node+strlen(MCABBER_CAPS_NODE)+1;
563   }
564   if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE))
565     disco_info_set_caps(query, param);  // client#version
566   else
567     // Basic discovery request
568     disco_info_set_caps(query, NULL);
569 
570   lm_connection_send(c, r, NULL);
571   lm_message_unref(r);
572   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
573 }
574 
handle_iq_roster(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)575 LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c,
576                                  LmMessage *m, gpointer ud)
577 {
578   LmMessageNode *y;
579   const char *fjid, *name, *group, *sub, *ask;
580   char *cleanalias;
581   enum subscr esub;
582   int need_refresh = FALSE;
583   guint roster_type;
584 
585   const gchar *from = lm_message_get_from(m);
586 
587   if (from) {
588     const gchar *self_jid = lm_connection_get_jid(c);
589     gchar *servername = get_servername(self_jid, "");
590     if ((!jid_equal(self_jid, from)) &&
591        (!servername || strcasecmp(from, servername))) {
592       scr_LogPrint(LPRINT_LOGNORM, "Received invalid roster IQ request");
593       g_free(servername);
594       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
595     }
596     g_free(servername);
597   }
598 
599   y = lm_message_node_get_child(lm_message_node_find_xmlns(m->node, NS_ROSTER),
600                                  "item");
601   for ( ; y; y = y->next) {
602     char *name_tmp = NULL;
603 
604     fjid = lm_message_node_get_attribute(y, "jid");
605     name = lm_message_node_get_attribute(y, "name");
606     sub = lm_message_node_get_attribute(y, "subscription");
607     ask = lm_message_node_get_attribute(y, "ask");
608 
609     if (lm_message_node_get_child(y, "group"))
610       group = lm_message_node_get_value(lm_message_node_get_child(y, "group"));
611     else
612       group = NULL;
613 
614     if (!fjid)
615       continue;
616 
617     cleanalias = jidtodisp(fjid);
618 
619     esub = sub_none;
620     if (sub) {
621       if (!strcmp(sub, "to"))          esub = sub_to;
622       else if (!strcmp(sub, "from"))   esub = sub_from;
623       else if (!strcmp(sub, "both"))   esub = sub_both;
624       else if (!strcmp(sub, "remove")) esub = sub_remove;
625     }
626 
627     if (esub == sub_remove) {
628       roster_del_user(cleanalias);
629       scr_LogPrint(LPRINT_LOGNORM, "Buddy <%s> has been removed "
630                    "from the roster", cleanalias);
631       g_free(cleanalias);
632       need_refresh = TRUE;
633       continue;
634     }
635 
636     if (ask && !strcmp(ask, "subscribe"))
637       esub |= sub_pending;
638 
639     if (!name) {
640       if (!settings_opt_get_int("roster_hide_domain")) {
641         name = cleanalias;
642       } else {
643         char *p;
644         name = name_tmp = g_strdup(cleanalias);
645         p = strchr(name_tmp, JID_DOMAIN_SEPARATOR);
646         if (p)  *p = '\0';
647       }
648     }
649 
650     // Tricky... :-\  My guess is that if there is no JID_DOMAIN_SEPARATOR,
651     // this is an agent.
652     if (strchr(cleanalias, JID_DOMAIN_SEPARATOR))
653       roster_type = ROSTER_TYPE_USER;
654     else
655       roster_type = ROSTER_TYPE_AGENT;
656 
657     roster_add_user(cleanalias, name, group, roster_type, esub, 1);
658 
659     g_free(name_tmp);
660     g_free(cleanalias);
661   }
662 
663   // Acknowledge IQ message
664   if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_SET) {
665     LmMessage *result;
666     result = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
667     lm_connection_send(c, result, NULL);
668     lm_message_unref(result);
669   }
670 
671   scr_update_roster();
672   if (need_refresh)
673     scr_update_buddy_window();
674   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
675 }
676 
handle_iq_ping(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)677 LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c,
678                                LmMessage *m, gpointer ud)
679 {
680   LmMessage *r;
681 
682   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
683   lm_connection_send(c, r, NULL);
684   lm_message_unref(r);
685   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
686 }
687 
seconds_since_last_use(void)688 double seconds_since_last_use(void)
689 {
690   return difftime(time(NULL), iqlast);
691 }
692 
handle_iq_last(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)693 LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c,
694                                LmMessage *m, gpointer ud)
695 {
696   LmMessage *r;
697   LmMessageNode *query;
698   char *seconds;
699 
700   if (!settings_opt_get_int("iq_hide_requests")) {
701     scr_LogPrint(LPRINT_LOGNORM, "Received an IQ last time request from <%s>",
702                  lm_message_get_from(m));
703   }
704 
705   if (settings_opt_get_int("iq_last_disable") ||
706       (settings_opt_get_int("iq_last_disable_when_notavail") &&
707        xmpp_getstatus() == notavail))
708   {
709     send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
710     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
711   }
712 
713   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
714   query = lm_message_node_add_child(r->node, "query", NULL);
715   lm_message_node_set_attribute(query, "xmlns", NS_LAST);
716   seconds = g_strdup_printf("%.0f", seconds_since_last_use());
717   lm_message_node_set_attribute(query, "seconds", seconds);
718   g_free(seconds);
719 
720   lm_connection_send(c, r, NULL);
721   lm_message_unref(r);
722   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
723 }
724 
handle_iq_version(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)725 LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c,
726                                   LmMessage *m, gpointer ud)
727 {
728   LmMessage *r;
729   LmMessageNode *query;
730 
731   if (!settings_opt_get_int("iq_hide_requests")) {
732     scr_LogPrint(LPRINT_LOGNORM, "Received an IQ version request from <%s>",
733                  lm_message_get_from(m));
734   }
735 
736   if (settings_opt_get_int("iq_version_hide")) {
737     send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
738     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
739   }
740 
741   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
742   query = lm_message_node_add_child(r->node, "query", NULL);
743   lm_message_node_set_attribute(query, "xmlns", NS_VERSION);
744 
745   lm_message_node_add_child(query, "name", PACKAGE_NAME);
746 
747   // MCabber version
748   if (!settings_opt_get_int("iq_version_hide_version")) {
749     char *ver = mcabber_version();
750     lm_message_node_add_child(query, "version", ver);
751     g_free(ver);
752   }
753 
754   // OS details
755   if (!settings_opt_get_int("iq_version_hide_os")) {
756     char *os;
757     struct utsname osinfo;
758     uname(&osinfo);
759     os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
760                          osinfo.machine);
761     lm_message_node_add_child(query, "os", os);
762     g_free(os);
763   }
764 
765   lm_connection_send(c, r, NULL);
766   lm_message_unref(r);
767   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
768 }
769 
770 // This function borrows some code from the Pidgin project
handle_iq_time(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)771 LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c,
772                                LmMessage *m, gpointer ud)
773 {
774   LmMessage *r;
775   LmMessageNode *query;
776   char *buf, *utf8_buf;
777   time_t now_t;
778   struct tm *now;
779 
780   if (!settings_opt_get_int("iq_hide_requests")) {
781     scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
782                  lm_message_get_from(m));
783   }
784 
785   if (settings_opt_get_int("iq_time_hide")) {
786     send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
787     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
788   }
789 
790   buf = g_new0(char, 512);
791 
792   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
793   query = lm_message_node_add_child(r->node, "query", NULL);
794   lm_message_node_set_attribute(query, "xmlns", NS_TIME);
795 
796   time(&now_t);
797   now = gmtime(&now_t);
798 
799   strftime(buf, 512, "%Y%m%dT%T", now);
800   lm_message_node_add_child(query, "utc", buf);
801 
802   now = localtime(&now_t);
803 
804   strftime(buf, 512, "%Z", now);
805   if ((utf8_buf = to_utf8(buf))) {
806     lm_message_node_add_child(query, "tz", utf8_buf);
807     g_free(utf8_buf);
808   }
809 
810   strftime(buf, 512, "%d %b %Y %T", now);
811   if ((utf8_buf = to_utf8(buf))) {
812     lm_message_node_add_child(query, "display", utf8_buf);
813     g_free(utf8_buf);
814   }
815 
816   lm_connection_send(c, r, NULL);
817   lm_message_unref(r);
818   g_free(buf);
819   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
820 }
821 
822 // This function borrows some code from the Pidgin project
handle_iq_time202(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)823 LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c,
824                                   LmMessage *m, gpointer ud)
825 {
826   LmMessage *r;
827   LmMessageNode *query;
828   char *buf, *utf8_buf;
829   time_t now_t;
830   struct tm *now;
831   char const *sign;
832   int diff = 0;
833 
834   if (!settings_opt_get_int("iq_hide_requests")) {
835     scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>",
836                  lm_message_get_from(m));
837   }
838 
839   if (settings_opt_get_int("iq_time_hide")) {
840     send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
841     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
842   }
843 
844   buf = g_new0(char, 512);
845 
846   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
847   query = lm_message_node_add_child(r->node, "time", NULL);
848   lm_message_node_set_attribute(query, "xmlns", NS_XMPP_TIME);
849 
850   time(&now_t);
851   now = localtime(&now_t);
852 
853   if (now->tm_isdst >= 0) {
854 #if defined HAVE_TM_GMTOFF
855     diff = now->tm_gmtoff;
856 #elif defined HAVE_TIMEZONE
857     tzset();
858     diff = -timezone;
859 #endif
860   }
861 
862   if (diff < 0) {
863     sign = "-";
864     diff = -diff;
865   } else {
866     sign = "+";
867   }
868   diff /= 60;
869   snprintf(buf, 512, "%c%02d:%02d", *sign, diff / 60, diff % 60);
870   if ((utf8_buf = to_utf8(buf))) {
871     lm_message_node_add_child(query, "tzo", utf8_buf);
872     g_free(utf8_buf);
873   }
874 
875   now = gmtime(&now_t);
876 
877   strftime(buf, 512, "%Y-%m-%dT%TZ", now);
878   lm_message_node_add_child(query, "utc", buf);
879 
880   lm_connection_send(c, r, NULL);
881   lm_message_unref(r);
882   g_free(buf);
883   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
884 }
885 
handle_iq_vcard(LmMessageHandler * h,LmConnection * c,LmMessage * m,gpointer ud)886 LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c,
887                                 LmMessage *m, gpointer ud)
888 {
889   send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE);
890   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
891 }
892 
893 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2:  For Vim users... */
894