comparison mcabber/src/xmpp_iq.c @ 1598:a087125d8fc8

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