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