You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
893 lines
29 KiB
893 lines
29 KiB
/* |
|
* xmpp_iq.c -- Jabber protocol IQ-related stuff |
|
* |
|
* Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de> |
|
* Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net> |
|
* Parts come from the centericq project: |
|
* Copyright (C) 2002-2005 by Konstantin Klyagin <konst@konst.org.ua> |
|
* Some small parts come from the Pidgin project <http://pidgin.im/> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or (at |
|
* your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, but |
|
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
* General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <string.h> |
|
#include <sys/utsname.h> |
|
|
|
#include "xmpp_helper.h" |
|
#include "commands.h" |
|
#include "screen.h" |
|
#include "utils.h" |
|
#include "logprint.h" |
|
#include "settings.h" |
|
#include "caps.h" |
|
#include "main.h" |
|
|
|
extern struct xmpp_error xmpp_errors[]; |
|
|
|
static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, |
|
gpointer ud); |
|
|
|
static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, |
|
gpointer ud); |
|
|
|
inline double seconds_since_last_use(void); |
|
|
|
struct adhoc_command { |
|
char *name; |
|
char *description; |
|
bool only_for_self; |
|
LmHandleMessageFunction callback; |
|
}; |
|
|
|
const struct adhoc_command adhoc_command_list[] = { |
|
{ "http://jabber.org/protocol/rc#set-status", |
|
"Change client status", |
|
1, |
|
&handle_iq_command_set_status }, |
|
{ "http://jabber.org/protocol/rc#leave-groupchats", |
|
"Leave groupchat(s)", |
|
1, |
|
&handle_iq_command_leave_groupchats }, |
|
{ NULL, NULL, 0, NULL }, |
|
}; |
|
|
|
struct adhoc_status { |
|
char *name; // the name used by adhoc |
|
char *description; |
|
char *status; // the string, used by setstus |
|
}; |
|
// It has to match imstatus of roster.h! |
|
const struct adhoc_status adhoc_status_list[] = { |
|
{"offline", "Offline", "offline"}, |
|
{"online", "Online", "avail"}, |
|
{"chat", "Chat", "free"}, |
|
{"dnd", "Do not disturb", "dnd"}, |
|
{"xa", "Extended away", "notavail"}, |
|
{"away", "Away", "away"}, |
|
#ifdef WITH_DEPRECATED_STATUS_INVISIBLE |
|
{"invisible", "Invisible", "invisible"}, |
|
#endif |
|
{NULL, NULL, NULL}, |
|
}; |
|
|
|
static char *generate_session_id(char *prefix) |
|
{ |
|
char *result; |
|
static int counter = 0; |
|
counter++; |
|
// TODO better use timestamp? |
|
result = g_strdup_printf("%s-%i", prefix, counter); |
|
return result; |
|
} |
|
|
|
static LmMessage *lm_message_new_iq_error(LmMessage *m, guint error) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *err; |
|
int i; |
|
|
|
if (G_UNLIKELY(!m)) return NULL; |
|
|
|
for (i = 0; xmpp_errors[i].code; ++i) |
|
if (xmpp_errors[i].code == error) |
|
break; |
|
g_return_val_if_fail(xmpp_errors[i].code > 0, NULL); |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_ERROR); |
|
err = lm_message_node_add_child(r->node, "error", NULL); |
|
lm_message_node_set_attribute(err, "code", xmpp_errors[i].code_str); |
|
lm_message_node_set_attribute(err, "type", xmpp_errors[i].type); |
|
lm_message_node_set_attribute |
|
(lm_message_node_add_child(err, |
|
xmpp_errors[i].condition, NULL), |
|
"xmlns", NS_XMPP_STANZAS); |
|
|
|
return r; |
|
} |
|
|
|
void send_iq_error(LmConnection *c, LmMessage *m, guint error) |
|
{ |
|
LmMessage *r; |
|
r = lm_message_new_iq_error(m, error); |
|
if (r) { |
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
} |
|
} |
|
|
|
static void lm_message_node_add_dataform_result(LmMessageNode *node, |
|
const char *message) |
|
{ |
|
LmMessageNode *x, *field; |
|
|
|
x = lm_message_node_add_child(node, "x", NULL); |
|
lm_message_node_set_attributes(x, |
|
"type", "result", |
|
"xmlns", "jabber:x:data", |
|
NULL); |
|
field = lm_message_node_add_child(x, "field", NULL); |
|
lm_message_node_set_attributes(field, |
|
"type", "text-single", |
|
"var", "message", |
|
NULL); |
|
lm_message_node_add_child(field, "value", message); |
|
} |
|
|
|
// Dummy handler to ignore IQ response |
|
LmHandlerResult handle_iq_dummy(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessageSubType mstype = lm_message_get_sub_type(m); |
|
if (mstype == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
display_server_error(lm_message_node_get_child(m->node, "error"), |
|
lm_message_get_from(m)); |
|
} |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult handle_iq_commands_list(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *iq; |
|
LmMessageNode *query; |
|
const char *requester_jid; |
|
const struct adhoc_command *command; |
|
const char *node; |
|
gboolean from_self; |
|
|
|
iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(iq->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_DISCO_ITEMS); |
|
node = lm_message_node_get_attribute |
|
(lm_message_node_get_child(m->node, "query"), |
|
"node"); |
|
if (node) |
|
lm_message_node_set_attribute(query, "node", node); |
|
|
|
requester_jid = lm_message_get_from(m); |
|
from_self = jid_equal(lm_connection_get_jid(c), requester_jid); |
|
|
|
for (command = adhoc_command_list ; command->name ; command++) { |
|
if (!command->only_for_self || from_self) { |
|
lm_message_node_set_attributes |
|
(lm_message_node_add_child(query, "item", NULL), |
|
"node", command->name, |
|
"name", command->description, |
|
"jid", lm_connection_get_jid(c), |
|
NULL); |
|
} |
|
} |
|
|
|
lm_connection_send(c, iq, NULL); |
|
lm_message_unref(iq); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult handle_iq_command_set_status(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
const char *action, *node; |
|
char *sessionid; |
|
LmMessage *iq; |
|
LmMessageNode *command, *x, *y; |
|
const struct adhoc_status *s; |
|
|
|
x = lm_message_node_get_child(m->node, "command"); |
|
action = lm_message_node_get_attribute(x, "action"); |
|
node = lm_message_node_get_attribute(x, "node"); |
|
sessionid = (char *)lm_message_node_get_attribute(x, "sessionid"); |
|
|
|
iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
command = lm_message_node_add_child(iq->node, "command", NULL); |
|
lm_message_node_set_attribute(command, "node", node); |
|
lm_message_node_set_attribute(command, "xmlns", NS_COMMANDS); |
|
|
|
if (!sessionid) { |
|
sessionid = generate_session_id("set-status"); |
|
lm_message_node_set_attribute(command, "sessionid", sessionid); |
|
g_free(sessionid); |
|
sessionid = NULL; |
|
lm_message_node_set_attribute(command, "status", "executing"); |
|
|
|
x = lm_message_node_add_child(command, "x", NULL); |
|
lm_message_node_set_attribute(x, "type", "form"); |
|
lm_message_node_set_attribute(x, "xmlns", "jabber:x:data"); |
|
|
|
lm_message_node_add_child(x, "title", "Change Status"); |
|
|
|
lm_message_node_add_child(x, "instructions", |
|
"Choose the status and status message"); |
|
|
|
// TODO see if factorisation is possible |
|
y = lm_message_node_add_child(x, "field", NULL); |
|
lm_message_node_set_attribute(y, "type", "hidden"); |
|
lm_message_node_set_attribute(y, "var", "FORM_TYPE"); |
|
|
|
lm_message_node_add_child(y, "value", "http://jabber.org/protocol/rc"); |
|
|
|
y = lm_message_node_add_child(x, "field", NULL); |
|
lm_message_node_set_attributes(y, |
|
"type", "list-single", |
|
"var", "status", |
|
"label", "Status", |
|
NULL); |
|
lm_message_node_add_child(y, "required", NULL); |
|
|
|
// XXX: ugly |
|
lm_message_node_add_child(y, "value", |
|
adhoc_status_list[xmpp_getstatus()].name); |
|
for (s = adhoc_status_list; s->name; s++) { |
|
LmMessageNode *option = lm_message_node_add_child(y, "option", NULL); |
|
lm_message_node_add_child(option, "value", s->name); |
|
lm_message_node_set_attribute(option, "label", s->description); |
|
} |
|
// TODO add priority ? |
|
// I do not think this is useful, user should not have to care of the |
|
// priority like gossip and gajim do (misc) |
|
lm_message_node_set_attributes |
|
(lm_message_node_add_child(x, "field", NULL), |
|
"type", "text-multi", |
|
"var", "status-message", |
|
"label", "Message", |
|
NULL); |
|
} else if (action && !strcmp(action, "cancel")) { |
|
lm_message_node_set_attribute(command, "status", "canceled"); |
|
} else { // (if sessionid and not canceled) |
|
y = lm_message_node_find_xmlns(x, "jabber:x:data"); //x?xmlns=jabber:x:data |
|
if (y) { |
|
const char *value=NULL, *message=NULL; |
|
LmMessageNode *fields, *field; |
|
field = fields = lm_message_node_get_child(y, "field"); //field?var=status |
|
while (field && strcmp("status", |
|
lm_message_node_get_attribute(field, "var"))) |
|
field = field->next; |
|
field = lm_message_node_get_child(field, "value"); |
|
if (field) |
|
value = lm_message_node_get_value(field); |
|
field = fields; //field?var=status-message |
|
while (field && strcmp("status-message", |
|
lm_message_node_get_attribute(field, "var"))) |
|
field = field->next; |
|
field = lm_message_node_get_child(field, "value"); |
|
if (field) |
|
message = lm_message_node_get_value(field); |
|
if (value) { |
|
for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++); |
|
if (s->name) { |
|
char *status = g_strdup_printf("%s %s", s->status, |
|
message ? message : ""); |
|
cmd_setstatus(NULL, status); |
|
g_free(status); |
|
lm_message_node_set_attribute(command, "status", "completed"); |
|
lm_message_node_add_dataform_result(command, |
|
"Status has been changed"); |
|
} |
|
} |
|
} |
|
} |
|
if (sessionid) |
|
lm_message_node_set_attribute(command, "sessionid", sessionid); |
|
lm_connection_send(c, iq, NULL); |
|
lm_message_unref(iq); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static void _callback_foreach_buddy_groupchat(gpointer rosterdata, void *param) |
|
{ |
|
LmMessageNode *field, *option; |
|
const char *room_jid, *nickname; |
|
char *desc; |
|
|
|
room_jid = buddy_getjid(rosterdata); |
|
if (!room_jid) return; |
|
nickname = buddy_getnickname(rosterdata); |
|
if (!nickname) return; |
|
field = param; |
|
|
|
option = lm_message_node_add_child(field, "option", NULL); |
|
lm_message_node_add_child(option, "value", room_jid); |
|
desc = g_strdup_printf("%s on %s", nickname, room_jid); |
|
lm_message_node_set_attribute(option, "label", desc); |
|
g_free(desc); |
|
} |
|
|
|
static LmHandlerResult handle_iq_command_leave_groupchats(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, |
|
gpointer ud) |
|
{ |
|
const char *action, *node; |
|
char *sessionid; |
|
LmMessage *iq; |
|
LmMessageNode *command, *x; |
|
|
|
x = lm_message_node_get_child(m->node, "command"); |
|
if (!x) |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
|
|
action = lm_message_node_get_attribute(x, "action"); |
|
node = lm_message_node_get_attribute(x, "node"); |
|
sessionid = (char*)lm_message_node_get_attribute(x, "sessionid"); |
|
|
|
iq = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
command = lm_message_node_add_child(iq->node, "command", NULL); |
|
lm_message_node_set_attributes(command, |
|
"node", node, |
|
"xmlns", NS_COMMANDS, |
|
NULL); |
|
|
|
if (!sessionid) { |
|
LmMessageNode *field; |
|
|
|
sessionid = generate_session_id("leave-groupchats"); |
|
lm_message_node_set_attribute(command, "sessionid", sessionid); |
|
g_free(sessionid); |
|
sessionid = NULL; |
|
lm_message_node_set_attribute(command, "status", "executing"); |
|
|
|
x = lm_message_node_add_child(command, "x", NULL); |
|
lm_message_node_set_attributes(x, |
|
"type", "form", |
|
"xmlns", "jabber:x:data", |
|
NULL); |
|
|
|
lm_message_node_add_child(x, "title", "Leave groupchat(s)"); |
|
|
|
lm_message_node_add_child(x, "instructions", |
|
"What groupchats do you want to leave?"); |
|
|
|
field = lm_message_node_add_child(x, "field", NULL); |
|
lm_message_node_set_attributes(field, |
|
"type", "hidden", |
|
"var", "FORM_TYPE", |
|
NULL); |
|
|
|
lm_message_node_add_child(field, "value", |
|
"http://jabber.org/protocol/rc"); |
|
|
|
field = lm_message_node_add_child(x, "field", NULL); |
|
lm_message_node_set_attributes(field, |
|
"type", "list-multi", |
|
"var", "groupchats", |
|
"label", "Groupchats: ", |
|
NULL); |
|
lm_message_node_add_child(field, "required", NULL); |
|
|
|
foreach_buddy(ROSTER_TYPE_ROOM, &_callback_foreach_buddy_groupchat, field); |
|
// TODO: return an error if we are not connected to groupchats |
|
} else if (action && !strcmp(action, "cancel")) { |
|
lm_message_node_set_attribute(command, "status", "canceled"); |
|
} else { // (if sessionid and not canceled) |
|
LmMessageNode *form = lm_message_node_find_xmlns(x, "jabber:x:data");// TODO |
|
if (form) { |
|
LmMessageNode *field; |
|
|
|
lm_message_node_set_attribute(command, "status", "completed"); |
|
// TODO: implement sth. like "field?var=groupchats" in xmlnode... |
|
field = lm_message_node_get_child(form, "field"); |
|
while (field && strcmp("groupchats", |
|
lm_message_node_get_attribute(field, "var"))) |
|
field = field->next; |
|
|
|
if (field) |
|
for (x = field->children ; x ; x = x->next) |
|
{ |
|
if (!strcmp (x->name, "value")) { |
|
GList* b = buddy_search_jid(lm_message_node_get_value(x)); |
|
if (b) |
|
cmd_room_leave(b->data, "Requested by remote command"); |
|
} |
|
} |
|
lm_message_node_add_dataform_result(command, |
|
"Groupchats have been left"); |
|
} |
|
} |
|
if (sessionid) |
|
lm_message_node_set_attribute(command, "sessionid", sessionid); |
|
lm_connection_send(c, iq, NULL); |
|
lm_message_unref(iq); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
LmHandlerResult handle_iq_commands(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
const char *requester_jid = NULL; |
|
LmMessageNode *cmd; |
|
const struct adhoc_command *command; |
|
|
|
// mcabber has only partial XEP-0146 support... |
|
if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type(m)) |
|
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
|
|
requester_jid = lm_message_get_from(m); |
|
|
|
cmd = lm_message_node_get_child(m->node, "command"); |
|
if (!cmd) { |
|
//send_iq_error(c, m, XMPP_ERROR_BAD_REQUEST); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
if (jid_equal(lm_connection_get_jid(c), requester_jid)) { |
|
const char *action, *node; |
|
action = lm_message_node_get_attribute(cmd, "action"); |
|
node = lm_message_node_get_attribute(cmd, "node"); |
|
// action can be NULL, in which case it seems to take the default, |
|
// ie execute |
|
if (!action || !strcmp(action, "execute") || !strcmp(action, "cancel") |
|
|| !strcmp(action, "next") || !strcmp(action, "complete")) { |
|
for (command = adhoc_command_list; command->name; command++) { |
|
if (!strcmp(node, command->name)) |
|
command->callback(h, c, m, ud); |
|
} |
|
// "prev" action will get there, as we do not implement it, |
|
// and do not authorize it |
|
} else { |
|
LmMessage *r; |
|
LmMessageNode *err; |
|
r = lm_message_new_iq_error(m, XMPP_ERROR_BAD_REQUEST); |
|
if (r) { |
|
err = lm_message_node_get_child(r->node, "error"); |
|
lm_message_node_set_attribute |
|
(lm_message_node_add_child(err, "malformed-action", NULL), |
|
"xmlns", NS_COMMANDS); |
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
} |
|
} |
|
} else { |
|
send_iq_error(c, m, XMPP_ERROR_FORBIDDEN); |
|
} |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
|
|
LmHandlerResult handle_iq_disco_items(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessageNode *query; |
|
const char *node = NULL; |
|
query = lm_message_node_get_child(m->node, "query"); |
|
if (query) |
|
node = lm_message_node_get_attribute(query, "node"); |
|
if (node) { |
|
if (!strcmp(node, NS_COMMANDS)) { |
|
return handle_iq_commands_list(NULL, c, m, ud); |
|
} else { |
|
send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED); |
|
} |
|
} else { |
|
// not sure about this one |
|
send_iq_error(c, m, XMPP_ERROR_NOT_IMPLEMENTED); |
|
} |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
|
|
void _disco_add_feature_helper(gpointer data, gpointer user_data) |
|
{ |
|
LmMessageNode *node = user_data; |
|
lm_message_node_set_attribute |
|
(lm_message_node_add_child(node, "feature", NULL), "var", data); |
|
} |
|
|
|
// disco_info_set_caps(ansquery, entitycaps) |
|
// Add features attributes to ansquery. entitycaps should either be a |
|
// valid capabilities hash or NULL. If it is NULL, the node attribute won't |
|
// be added to the query child and Entity Capabilities will be announced |
|
// as a feature. |
|
// Please change the entity version string if you modify mcabber disco |
|
// source code, so that it doesn't conflict with the upstream client. |
|
static void disco_info_set_caps(LmMessageNode *ansquery, |
|
const char *entitycaps) |
|
{ |
|
if (entitycaps) { |
|
char *eversion; |
|
eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entitycaps); |
|
lm_message_node_set_attribute(ansquery, "node", eversion); |
|
g_free(eversion); |
|
} |
|
|
|
lm_message_node_set_attributes |
|
(lm_message_node_add_child(ansquery, "identity", NULL), |
|
"category", "client", |
|
"name", "mcabber", |
|
"type", "pc", |
|
NULL); |
|
|
|
if (entitycaps) |
|
caps_foreach_feature(entitycaps, _disco_add_feature_helper, ansquery); |
|
else |
|
caps_foreach_feature(entity_version(xmpp_getstatus()), |
|
_disco_add_feature_helper, |
|
ansquery); |
|
} |
|
|
|
LmHandlerResult handle_iq_disco_info(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *query, *tmp; |
|
const char *node = NULL; |
|
const char *param = NULL; |
|
|
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_RESULT) |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(r->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_DISCO_INFO); |
|
tmp = lm_message_node_get_child(m->node, "query"); |
|
if (tmp) { |
|
node = lm_message_node_get_attribute(tmp, "node"); |
|
param = node+strlen(MCABBER_CAPS_NODE)+1; |
|
} |
|
if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE)) |
|
disco_info_set_caps(query, param); // client#version |
|
else |
|
// Basic discovery request |
|
disco_info_set_caps(query, NULL); |
|
|
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
LmHandlerResult handle_iq_roster(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessageNode *y; |
|
const char *fjid, *name, *group, *sub, *ask; |
|
char *cleanalias; |
|
enum subscr esub; |
|
int need_refresh = FALSE; |
|
guint roster_type; |
|
|
|
const gchar *from = lm_message_get_from(m); |
|
|
|
if (from) { |
|
const gchar *self_jid = lm_connection_get_jid(c); |
|
gchar *servername = get_servername(self_jid, ""); |
|
if ((!jid_equal(self_jid, from)) && |
|
(!servername || strcasecmp(from, servername))) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received invalid roster IQ request"); |
|
g_free(servername); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
g_free(servername); |
|
} |
|
|
|
y = lm_message_node_get_child(lm_message_node_find_xmlns(m->node, NS_ROSTER), |
|
"item"); |
|
for ( ; y; y = y->next) { |
|
char *name_tmp = NULL; |
|
|
|
fjid = lm_message_node_get_attribute(y, "jid"); |
|
name = lm_message_node_get_attribute(y, "name"); |
|
sub = lm_message_node_get_attribute(y, "subscription"); |
|
ask = lm_message_node_get_attribute(y, "ask"); |
|
|
|
if (lm_message_node_get_child(y, "group")) |
|
group = lm_message_node_get_value(lm_message_node_get_child(y, "group")); |
|
else |
|
group = NULL; |
|
|
|
if (!fjid) |
|
continue; |
|
|
|
cleanalias = jidtodisp(fjid); |
|
|
|
esub = sub_none; |
|
if (sub) { |
|
if (!strcmp(sub, "to")) esub = sub_to; |
|
else if (!strcmp(sub, "from")) esub = sub_from; |
|
else if (!strcmp(sub, "both")) esub = sub_both; |
|
else if (!strcmp(sub, "remove")) esub = sub_remove; |
|
} |
|
|
|
if (esub == sub_remove) { |
|
roster_del_user(cleanalias); |
|
scr_LogPrint(LPRINT_LOGNORM, "Buddy <%s> has been removed " |
|
"from the roster", cleanalias); |
|
g_free(cleanalias); |
|
need_refresh = TRUE; |
|
continue; |
|
} |
|
|
|
if (ask && !strcmp(ask, "subscribe")) |
|
esub |= sub_pending; |
|
|
|
if (!name) { |
|
if (!settings_opt_get_int("roster_hide_domain")) { |
|
name = cleanalias; |
|
} else { |
|
char *p; |
|
name = name_tmp = g_strdup(cleanalias); |
|
p = strchr(name_tmp, JID_DOMAIN_SEPARATOR); |
|
if (p) *p = '\0'; |
|
} |
|
} |
|
|
|
// Tricky... :-\ My guess is that if there is no JID_DOMAIN_SEPARATOR, |
|
// this is an agent. |
|
if (strchr(cleanalias, JID_DOMAIN_SEPARATOR)) |
|
roster_type = ROSTER_TYPE_USER; |
|
else |
|
roster_type = ROSTER_TYPE_AGENT; |
|
|
|
roster_add_user(cleanalias, name, group, roster_type, esub, 1); |
|
|
|
g_free(name_tmp); |
|
g_free(cleanalias); |
|
} |
|
|
|
// Acknowledge IQ message |
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_SET) { |
|
LmMessage *result; |
|
result = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
lm_connection_send(c, result, NULL); |
|
lm_message_unref(result); |
|
} |
|
|
|
scr_update_roster(); |
|
if (need_refresh) |
|
scr_update_buddy_window(); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
LmHandlerResult handle_iq_ping(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
double seconds_since_last_use(void) |
|
{ |
|
return difftime(time(NULL), iqlast); |
|
} |
|
|
|
LmHandlerResult handle_iq_last(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *query; |
|
char *seconds; |
|
|
|
if (!settings_opt_get_int("iq_hide_requests")) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received an IQ last time request from <%s>", |
|
lm_message_get_from(m)); |
|
} |
|
|
|
if (settings_opt_get_int("iq_last_disable") || |
|
(settings_opt_get_int("iq_last_disable_when_notavail") && |
|
xmpp_getstatus() == notavail)) |
|
{ |
|
send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(r->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_LAST); |
|
seconds = g_strdup_printf("%.0f", seconds_since_last_use()); |
|
lm_message_node_set_attribute(query, "seconds", seconds); |
|
g_free(seconds); |
|
|
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
LmHandlerResult handle_iq_version(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *query; |
|
|
|
if (!settings_opt_get_int("iq_hide_requests")) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received an IQ version request from <%s>", |
|
lm_message_get_from(m)); |
|
} |
|
|
|
if (settings_opt_get_int("iq_version_hide")) { |
|
send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(r->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_VERSION); |
|
|
|
lm_message_node_add_child(query, "name", "mcabber"); |
|
|
|
// MCabber version |
|
if (!settings_opt_get_int("iq_version_hide_version")) { |
|
char *ver = mcabber_version(); |
|
lm_message_node_add_child(query, "version", ver); |
|
g_free(ver); |
|
} |
|
|
|
// OS details |
|
if (!settings_opt_get_int("iq_version_hide_os")) { |
|
char *os; |
|
struct utsname osinfo; |
|
uname(&osinfo); |
|
os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release, |
|
osinfo.machine); |
|
lm_message_node_add_child(query, "os", os); |
|
g_free(os); |
|
} |
|
|
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// This function borrows some code from the Pidgin project |
|
LmHandlerResult handle_iq_time(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *query; |
|
char *buf, *utf8_buf; |
|
time_t now_t; |
|
struct tm *now; |
|
|
|
if (!settings_opt_get_int("iq_hide_requests")) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>", |
|
lm_message_get_from(m)); |
|
} |
|
|
|
if (settings_opt_get_int("iq_time_hide")) { |
|
send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_new0(char, 512); |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(r->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_TIME); |
|
|
|
time(&now_t); |
|
now = gmtime(&now_t); |
|
|
|
strftime(buf, 512, "%Y%m%dT%T", now); |
|
lm_message_node_add_child(query, "utc", buf); |
|
|
|
now = localtime(&now_t); |
|
|
|
strftime(buf, 512, "%Z", now); |
|
if ((utf8_buf = to_utf8(buf))) { |
|
lm_message_node_add_child(query, "tz", utf8_buf); |
|
g_free(utf8_buf); |
|
} |
|
|
|
strftime(buf, 512, "%d %b %Y %T", now); |
|
if ((utf8_buf = to_utf8(buf))) { |
|
lm_message_node_add_child(query, "display", utf8_buf); |
|
g_free(utf8_buf); |
|
} |
|
|
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
g_free(buf); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// This function borrows some code from the Pidgin project |
|
LmHandlerResult handle_iq_time202(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
LmMessage *r; |
|
LmMessageNode *query; |
|
char *buf, *utf8_buf; |
|
time_t now_t; |
|
struct tm *now; |
|
char const *sign; |
|
int diff = 0; |
|
|
|
if (!settings_opt_get_int("iq_hide_requests")) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received an IQ time request from <%s>", |
|
lm_message_get_from(m)); |
|
} |
|
|
|
if (settings_opt_get_int("iq_time_hide")) { |
|
send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_new0(char, 512); |
|
|
|
r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT); |
|
query = lm_message_node_add_child(r->node, "time", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_XMPP_TIME); |
|
|
|
time(&now_t); |
|
now = localtime(&now_t); |
|
|
|
if (now->tm_isdst >= 0) { |
|
#if defined HAVE_TM_GMTOFF |
|
diff = now->tm_gmtoff; |
|
#elif defined HAVE_TIMEZONE |
|
tzset(); |
|
diff = -timezone; |
|
#endif |
|
} |
|
|
|
if (diff < 0) { |
|
sign = "-"; |
|
diff = -diff; |
|
} else { |
|
sign = "+"; |
|
} |
|
diff /= 60; |
|
snprintf(buf, 512, "%c%02d:%02d", *sign, diff / 60, diff % 60); |
|
if ((utf8_buf = to_utf8(buf))) { |
|
lm_message_node_add_child(query, "tzo", utf8_buf); |
|
g_free(utf8_buf); |
|
} |
|
|
|
now = gmtime(&now_t); |
|
|
|
strftime(buf, 512, "%Y-%m-%dT%TZ", now); |
|
lm_message_node_add_child(query, "utc", buf); |
|
|
|
lm_connection_send(c, r, NULL); |
|
lm_message_unref(r); |
|
g_free(buf); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
LmHandlerResult handle_iq_vcard(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer ud) |
|
{ |
|
send_iq_error(c, m, XMPP_ERROR_SERVICE_UNAVAILABLE); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
/* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */
|
|
|