comparison mcabber/mcabber/xmpp_iq.c @ 1668:41c26b7d2890

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