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.
805 lines
27 KiB
805 lines
27 KiB
/* |
|
* xmpp_iqrequest.c -- Jabber IQ request handling |
|
* |
|
* Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de> |
|
* Copyright (C) 2005-2014 Mikael Berthe <mikael@lilotux.net> |
|
* |
|
* 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 <stdlib.h> |
|
#include <sys/time.h> |
|
|
|
#include "xmpp_helper.h" |
|
#include "xmpp_iq.h" |
|
#include "screen.h" |
|
#include "utils.h" |
|
#include "settings.h" |
|
#include "hooks.h" |
|
#include "hbuf.h" |
|
#include "carbons.h" |
|
|
|
extern LmMessageNode *bookmarks; |
|
extern LmMessageNode *rosternotes; |
|
|
|
static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_ping(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
static LmHandlerResult cb_disco_info(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data); |
|
|
|
|
|
static struct IqRequestHandlers |
|
{ |
|
const gchar *xmlns; |
|
const gchar *querytag; |
|
LmHandleMessageFunction handler; |
|
} iq_request_handlers[] = { |
|
{NS_ROSTER, "query", &cb_roster}, |
|
{NS_VERSION, "query", &cb_version}, |
|
{NS_XMPP_TIME,"time", &cb_time}, |
|
{NS_LAST, "query", &cb_last}, |
|
{NS_PING, "ping", &cb_ping}, |
|
{NS_VCARD, "vCard", &cb_vcard}, |
|
{NS_DISCO_INFO, "query", &cb_disco_info}, |
|
{NULL, NULL, NULL} |
|
}; |
|
|
|
// Enum for vCard attributes |
|
enum vcard_attr { |
|
vcard_home = 1<<0, |
|
vcard_work = 1<<1, |
|
vcard_postal = 1<<2, |
|
vcard_voice = 1<<3, |
|
vcard_fax = 1<<4, |
|
vcard_cell = 1<<5, |
|
vcard_inet = 1<<6, |
|
vcard_pref = 1<<7, |
|
}; |
|
|
|
static LmHandlerResult cb_disco_info(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
LmMessageNode *feature; |
|
|
|
ansqry = lm_message_node_get_child(m->node, "query"); |
|
|
|
feature = lm_message_node_get_child(ansqry, "feature"); |
|
for (; feature; feature = feature->next) { |
|
const char *v = lm_message_node_get_attribute(feature, "var"); |
|
|
|
if (!g_strcmp0(v, NS_CARBONS_2)) { |
|
carbons_available(); |
|
} |
|
} |
|
|
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult cb_ping(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
struct timeval *timestamp = (struct timeval *)user_data; |
|
struct timeval now; |
|
time_t dsec; |
|
suseconds_t dusec; |
|
const gchar *fjid; |
|
gchar *bjid, *mesg = NULL; |
|
|
|
gettimeofday(&now, NULL); |
|
dsec = now.tv_sec - timestamp->tv_sec; |
|
if (now.tv_usec < timestamp->tv_usec) { |
|
dusec = now.tv_usec + 1000000 - timestamp->tv_usec; |
|
--dsec; |
|
} else |
|
dusec = now.tv_usec - timestamp->tv_usec; |
|
|
|
// Check IQ result sender |
|
fjid = lm_message_get_from(m); |
|
if (!fjid) |
|
fjid = lm_connection_get_jid(lconnection); // No from means our JID... |
|
if (!fjid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name)."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
bjid = jidtodisp(fjid); |
|
|
|
switch (lm_message_get_sub_type(m)) { |
|
case LM_MESSAGE_SUB_TYPE_RESULT: |
|
mesg = g_strdup_printf("Pong from <%s>: %d second%s %d ms.", fjid, |
|
(int)dsec, dsec > 1 ? "s" : "", |
|
(int)(dusec/1000L)); |
|
break; |
|
|
|
case LM_MESSAGE_SUB_TYPE_ERROR: |
|
display_server_error(lm_message_node_get_child(m->node, "error"), |
|
fjid); |
|
mesg = g_strdup_printf("Ping to <%s> failed. " |
|
"Response time: %d second%s %d ms.", |
|
fjid, (int)dsec, dsec > 1 ? "s" : "", |
|
(int)(dusec/1000L)); |
|
break; |
|
|
|
default: |
|
g_free(bjid); |
|
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
break; |
|
} |
|
|
|
if (mesg) |
|
scr_WriteIncomingMessage(bjid, mesg, 0, HBB_PREFIX_INFO, 0); |
|
g_free(mesg); |
|
g_free(bjid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Warning!! xmlns has to be a namespace from iq_request_handlers[].xmlns |
|
void xmpp_iq_request(const char *fulljid, const char *xmlns) |
|
{ |
|
LmMessage *iq; |
|
LmMessageNode *query; |
|
LmMessageHandler *handler; |
|
gpointer data = NULL; |
|
GDestroyNotify notifier = NULL; |
|
GError *error = NULL; |
|
int i; |
|
|
|
iq = lm_message_new_with_sub_type(fulljid, LM_MESSAGE_TYPE_IQ, |
|
LM_MESSAGE_SUB_TYPE_GET); |
|
for (i = 0; iq_request_handlers[i].xmlns && |
|
strcmp(iq_request_handlers[i].xmlns, xmlns) != 0 ; ++i) |
|
; |
|
query = lm_message_node_add_child(iq->node, |
|
iq_request_handlers[i].querytag, |
|
NULL); |
|
lm_message_node_set_attribute(query, "xmlns", xmlns); |
|
|
|
if (!g_strcmp0(xmlns, NS_PING)) { // Create handler for ping queries |
|
struct timeval *now = g_new(struct timeval, 1); |
|
gettimeofday(now, NULL); |
|
data = (gpointer)now; |
|
notifier = g_free; |
|
} else if (!g_strcmp0(xmlns, NS_DISCO_INFO)) { |
|
gchar *servername = get_servername(settings_opt_get("jid"), |
|
settings_opt_get("server")); |
|
lm_message_node_set_attribute(iq->node, "to", servername); |
|
g_free(servername); |
|
} |
|
|
|
handler = lm_message_handler_new(iq_request_handlers[i].handler, |
|
data, notifier); |
|
|
|
lm_connection_send_with_reply(lconnection, iq, handler, &error); |
|
lm_message_handler_unref(handler); |
|
lm_message_unref(iq); |
|
|
|
if (error) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Error sending IQ request: %s.", error->message); |
|
g_error_free(error); |
|
} |
|
} |
|
|
|
// This callback is reached when mcabber receives the first roster update |
|
// after the connection. |
|
static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *x; |
|
const char *ns; |
|
|
|
// Only execute the hook if the roster has been successfully retrieved |
|
if (lm_message_get_sub_type(m) != LM_MESSAGE_SUB_TYPE_RESULT) |
|
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
|
|
x = lm_message_node_get_child(m->node, "query"); |
|
if (!x) |
|
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
|
|
ns = lm_message_node_get_attribute(x, "xmlns"); |
|
if (ns && !strcmp(ns, NS_ROSTER)) |
|
handle_iq_roster(NULL, c, m, user_data); |
|
|
|
// Post-login stuff |
|
hk_postconnect(); |
|
|
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
const char *p, *bjid; |
|
char *bare_jid; |
|
char *buf; |
|
|
|
// Check IQ result sender |
|
bjid = lm_message_get_from(m); |
|
if (!bjid) |
|
bjid = lm_connection_get_jid(lconnection); // No from means our JID... |
|
if (!bjid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name)."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check for error message |
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received error IQ message (%s)", bjid); |
|
display_server_error(lm_message_node_get_child(m->node, "error"), NULL); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check message contents |
|
ansqry = lm_message_node_get_child(m->node, "query"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result from <%s>!", bjid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_strdup_printf("Received IQ:version result from <%s>", bjid); |
|
scr_LogPrint(LPRINT_LOGNORM, "%s", buf); |
|
|
|
// bjid should now really be the "bare JID", let's strip the resource |
|
bare_jid = jidtodisp(bjid); |
|
|
|
scr_WriteIncomingMessage(bare_jid, buf, 0, HBB_PREFIX_INFO, 0); |
|
g_free(buf); |
|
|
|
// Get result data... |
|
p = lm_message_node_get_child_value(ansqry, "name"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("Name: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
p = lm_message_node_get_child_value(ansqry, "version"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("Version: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
p = lm_message_node_get_child_value(ansqry, "os"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("OS: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
g_free(bare_jid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
const char *p, *bjid; |
|
char *bare_jid; |
|
char *buf; |
|
|
|
// Check IQ result sender |
|
bjid = lm_message_get_from(m); |
|
if (!bjid) |
|
bjid = lm_connection_get_jid(lconnection); // No from means our JID... |
|
if (!bjid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result (no sender name)."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check for error message |
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received error IQ message (%s)", bjid); |
|
display_server_error(lm_message_node_get_child(m->node, "error"), NULL); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check message contents |
|
ansqry = lm_message_node_get_child(m->node, "time"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result from <%s>!", bjid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_strdup_printf("Received IQ:time result from <%s>", bjid); |
|
scr_LogPrint(LPRINT_LOGNORM, "%s", buf); |
|
|
|
// bjid should now really be the "bare JID", let's strip the resource |
|
bare_jid = jidtodisp(bjid); |
|
|
|
scr_WriteIncomingMessage(bare_jid, buf, 0, HBB_PREFIX_INFO, 0); |
|
g_free(buf); |
|
|
|
// Get result data... |
|
p = lm_message_node_get_child_value(ansqry, "utc"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("UTC: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
p = lm_message_node_get_child_value(ansqry, "tzo"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("TZ: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
p = lm_message_node_get_child_value(ansqry, "display"); |
|
if (p && *p) { |
|
buf = g_strdup_printf("Time: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
g_free(bare_jid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
const char *p, *bjid; |
|
char *bare_jid; |
|
char *buf; |
|
|
|
// Check IQ result sender |
|
bjid = lm_message_get_from(m); |
|
if (!bjid) |
|
bjid = lm_connection_get_jid(lconnection); // No from means our JID... |
|
if (!bjid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result (no sender name)."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check for error message |
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received error IQ message (%s)", bjid); |
|
display_server_error(lm_message_node_get_child(m->node, "error"), NULL); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check message contents |
|
ansqry = lm_message_node_get_child(m->node, "query"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result from <%s>!", bjid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_strdup_printf("Received IQ:last result from <%s>", bjid); |
|
scr_LogPrint(LPRINT_LOGNORM, "%s", buf); |
|
|
|
// bjid should now really be the "bare JID", let's strip the resource |
|
bare_jid = jidtodisp(bjid); |
|
|
|
scr_WriteIncomingMessage(bare_jid, buf, 0, HBB_PREFIX_INFO, 0); |
|
g_free(buf); |
|
|
|
// Get result data... |
|
p = lm_message_node_get_attribute(ansqry, "seconds"); |
|
if (p) { |
|
long int s; |
|
GString *sbuf; |
|
sbuf = g_string_new("Idle time: "); |
|
s = atol(p); |
|
// Days |
|
if (s > 86400L) { |
|
g_string_append_printf(sbuf, "%ldd ", s/86400L); |
|
s %= 86400L; |
|
} |
|
// hh:mm:ss |
|
g_string_append_printf(sbuf, "%02ld:", s/3600L); |
|
s %= 3600L; |
|
g_string_append_printf(sbuf, "%02ld:%02ld", s/60L, s%60L); |
|
scr_WriteIncomingMessage(bare_jid, sbuf->str, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_string_free(sbuf, TRUE); |
|
} else { |
|
scr_WriteIncomingMessage(bare_jid, "No idle time reported.", |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
p = lm_message_node_get_value(ansqry); |
|
if (p) { |
|
buf = g_strdup_printf("Status message: %s", p); |
|
scr_WriteIncomingMessage(bare_jid, buf, 0, HBB_PREFIX_INFO, 0); |
|
g_free(buf); |
|
} |
|
g_free(bare_jid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static void display_vcard_item(const char *bjid, const char *label, |
|
enum vcard_attr vcard_attrib, const char *text) |
|
{ |
|
char *buf; |
|
|
|
if (!text || !*text || !bjid || !label) |
|
return; |
|
|
|
buf = g_strdup_printf("%s: %s%s%s%s%s%s%s%s%s%s", label, |
|
(vcard_attrib & vcard_home ? "[home]" : ""), |
|
(vcard_attrib & vcard_work ? "[work]" : ""), |
|
(vcard_attrib & vcard_postal ? "[postal]" : ""), |
|
(vcard_attrib & vcard_voice ? "[voice]" : ""), |
|
(vcard_attrib & vcard_fax ? "[fax]" : ""), |
|
(vcard_attrib & vcard_cell ? "[cell]" : ""), |
|
(vcard_attrib & vcard_inet ? "[inet]" : ""), |
|
(vcard_attrib & vcard_pref ? "[pref]" : ""), |
|
(vcard_attrib ? " " : ""), |
|
text); |
|
scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
g_free(buf); |
|
} |
|
|
|
static void handle_vcard_node(const char *barejid, LmMessageNode *vcardnode) |
|
{ |
|
LmMessageNode *x; |
|
const char *p; |
|
|
|
for (x = vcardnode->children ; x; x = x->next) { |
|
const char *data; |
|
enum vcard_attr vcard_attrib = 0; |
|
|
|
p = x->name; |
|
if (!p) |
|
continue; |
|
|
|
data = lm_message_node_get_value(x); |
|
|
|
if (!strcmp(p, "FN")) |
|
display_vcard_item(barejid, "Name", vcard_attrib, data); |
|
else if (!strcmp(p, "NICKNAME")) |
|
display_vcard_item(barejid, "Nickname", vcard_attrib, data); |
|
else if (!strcmp(p, "URL")) |
|
display_vcard_item(barejid, "URL", vcard_attrib, data); |
|
else if (!strcmp(p, "BDAY")) |
|
display_vcard_item(barejid, "Birthday", vcard_attrib, data); |
|
else if (!strcmp(p, "TZ")) |
|
display_vcard_item(barejid, "Timezone", vcard_attrib, data); |
|
else if (!strcmp(p, "TITLE")) |
|
display_vcard_item(barejid, "Title", vcard_attrib, data); |
|
else if (!strcmp(p, "ROLE")) |
|
display_vcard_item(barejid, "Role", vcard_attrib, data); |
|
else if (!strcmp(p, "DESC")) |
|
display_vcard_item(barejid, "Comment", vcard_attrib, data); |
|
else if (!strcmp(p, "N")) { |
|
data = lm_message_node_get_child_value(x, "FAMILY"); |
|
display_vcard_item(barejid, "Family Name", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "GIVEN"); |
|
display_vcard_item(barejid, "Given Name", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "MIDDLE"); |
|
display_vcard_item(barejid, "Middle Name", vcard_attrib, data); |
|
} else if (!strcmp(p, "ORG")) { |
|
data = lm_message_node_get_child_value(x, "ORGNAME"); |
|
display_vcard_item(barejid, "Organisation name", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "ORGUNIT"); |
|
display_vcard_item(barejid, "Organisation unit", vcard_attrib, data); |
|
} else { |
|
// The HOME, WORK and PREF attributes are common to the remaining fields |
|
// (ADR, TEL & EMAIL) |
|
if (lm_message_node_get_child(x, "HOME")) |
|
vcard_attrib |= vcard_home; |
|
if (lm_message_node_get_child(x, "WORK")) |
|
vcard_attrib |= vcard_work; |
|
if (lm_message_node_get_child(x, "PREF")) |
|
vcard_attrib |= vcard_pref; |
|
if (!strcmp(p, "ADR")) { // Address |
|
if (lm_message_node_get_child(x, "POSTAL")) |
|
vcard_attrib |= vcard_postal; |
|
data = lm_message_node_get_child_value(x, "EXTADD"); |
|
display_vcard_item(barejid, "Addr (ext)", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "STREET"); |
|
display_vcard_item(barejid, "Street", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "LOCALITY"); |
|
display_vcard_item(barejid, "Locality", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "REGION"); |
|
display_vcard_item(barejid, "Region", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "PCODE"); |
|
display_vcard_item(barejid, "Postal code", vcard_attrib, data); |
|
data = lm_message_node_get_child_value(x, "CTRY"); |
|
display_vcard_item(barejid, "Country", vcard_attrib, data); |
|
} else if (!strcmp(p, "TEL")) { // Telephone |
|
data = lm_message_node_get_child_value(x, "NUMBER"); |
|
if (data) { |
|
if (lm_message_node_get_child(x, "VOICE")) |
|
vcard_attrib |= vcard_voice; |
|
if (lm_message_node_get_child(x, "FAX")) |
|
vcard_attrib |= vcard_fax; |
|
if (lm_message_node_get_child(x, "CELL")) |
|
vcard_attrib |= vcard_cell; |
|
display_vcard_item(barejid, "Phone", vcard_attrib, data); |
|
} |
|
} else if (!strcmp(p, "EMAIL")) { // Email |
|
if (lm_message_node_get_child(x, "INTERNET")) |
|
vcard_attrib |= vcard_inet; |
|
data = lm_message_node_get_child_value(x, "USERID"); |
|
display_vcard_item(barejid, "Email", vcard_attrib, data); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
const char *bjid; |
|
char *bare_jid; |
|
char *buf; |
|
|
|
// Check IQ result sender |
|
bjid = lm_message_get_from(m); |
|
if (!bjid) |
|
bjid = lm_connection_get_jid(lconnection); // No from means our JID... |
|
if (!bjid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:vCard result (no sender name)."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// Check for error message |
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Received error IQ message (%s)", bjid); |
|
display_server_error(lm_message_node_get_child(m->node, "error"), NULL); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
buf = g_strdup_printf("Received IQ:vCard result from <%s>", bjid); |
|
scr_LogPrint(LPRINT_LOGNORM, "%s", buf); |
|
|
|
// Get the vCard node |
|
ansqry = lm_message_node_get_child(m->node, "vCard"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Empty IQ:vCard result!"); |
|
g_free(buf); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
// bjid should really be the "bare JID", let's strip the resource |
|
bare_jid = jidtodisp(bjid); |
|
|
|
scr_WriteIncomingMessage(bare_jid, buf, 0, HBB_PREFIX_INFO, 0); |
|
g_free(buf); |
|
|
|
// Get result data... |
|
handle_vcard_node(bare_jid, ansqry); |
|
g_free(bare_jid); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
static void storage_bookmarks_parse_conference(LmMessageNode *node) |
|
{ |
|
const char *fjid, *name, *autojoin; |
|
const char *pstatus, *awhois, *fjoins, *group; |
|
char *bjid; |
|
GSList *room_elt; |
|
|
|
fjid = lm_message_node_get_attribute(node, "jid"); |
|
if (!fjid) |
|
return; |
|
name = lm_message_node_get_attribute(node, "name"); |
|
autojoin = lm_message_node_get_attribute(node, "autojoin"); |
|
awhois = lm_message_node_get_attribute(node, "autowhois"); |
|
pstatus = lm_message_node_get_child_value(node, "print_status"); |
|
fjoins = lm_message_node_get_child_value(node, "flag_joins"); |
|
group = lm_message_node_get_child_value(node, "group"); |
|
|
|
bjid = jidtodisp(fjid); // Bare jid |
|
|
|
// Make sure this is a room (it can be a conversion user->room) |
|
room_elt = roster_find(bjid, jidsearch, 0); |
|
if (!room_elt) { |
|
room_elt = roster_add_user(bjid, name, group, ROSTER_TYPE_ROOM, |
|
sub_none, -1); |
|
} else { |
|
buddy_settype(room_elt->data, ROSTER_TYPE_ROOM); |
|
/* |
|
// If the name is available, should we use it? |
|
// I don't think so, it would be confusing because this item is already |
|
// in the roster. |
|
if (name) |
|
buddy_setname(room_elt->data, name); |
|
|
|
// The same question for roster group. |
|
if (group) |
|
buddy_setgroup(room_elt->data, group); |
|
*/ |
|
} |
|
|
|
// Set the print_status and auto_whois values |
|
if (pstatus) { |
|
enum room_printstatus i; |
|
for (i = status_none; i <= status_all; i++) |
|
if (!strcasecmp(pstatus, strprintstatus[i])) |
|
break; |
|
if (i <= status_all) |
|
buddy_setprintstatus(room_elt->data, i); |
|
} |
|
if (awhois) { |
|
enum room_autowhois i = autowhois_default; |
|
if (!strcmp(awhois, "1") || !(strcmp(awhois, "true"))) |
|
i = autowhois_on; |
|
else if (!strcmp(awhois, "0") || !(strcmp(awhois, "false"))) |
|
i = autowhois_off; |
|
if (i != autowhois_default) |
|
buddy_setautowhois(room_elt->data, i); |
|
} |
|
if (fjoins) { |
|
enum room_flagjoins i; |
|
for (i = flagjoins_none; i <= flagjoins_all; i++) |
|
if (!strcasecmp(fjoins, strflagjoins[i])) |
|
break; |
|
if (i <= flagjoins_all) |
|
buddy_setflagjoins(room_elt->data, i); |
|
} |
|
|
|
// Is autojoin set? |
|
// If it is, we'll look up for more information (nick? password?) and |
|
// try to join the room. |
|
if (autojoin && (!strcmp(autojoin, "1") || !strcmp(autojoin, "true"))) { |
|
const char *nick, *passwd; |
|
char *tmpnick = NULL; |
|
nick = lm_message_node_get_child_value(node, "nick"); |
|
passwd = lm_message_node_get_child_value(node, "password"); |
|
if (!nick || !*nick) |
|
nick = tmpnick = default_muc_nickname(NULL); |
|
// Let's join now |
|
scr_LogPrint(LPRINT_LOGNORM, "Auto-join bookmark <%s>", bjid); |
|
xmpp_room_join(bjid, nick, passwd); |
|
g_free(tmpnick); |
|
} |
|
g_free(bjid); |
|
|
|
buddylist_defer_build(); |
|
scr_update_roster(); |
|
} |
|
|
|
static LmHandlerResult cb_storage_bookmarks(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *x, *ansqry; |
|
char *p = NULL; |
|
|
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
LmMessageNode *error = lm_message_node_get_child(m->node, "error"); |
|
// No server support, or no bookmarks? |
|
if (error && error->children) |
|
p = error->children->name; |
|
if (p && !strcmp(p, "item-not-found")) { |
|
// item-no-found means the server has Private Storage, but it's |
|
// currently empty. |
|
if (bookmarks) |
|
lm_message_node_unref(bookmarks); |
|
bookmarks = lm_message_node_new("storage", "storage:bookmarks"); |
|
// We return 0 so that the IQ error message be |
|
// not displayed, as it isn't a real error. |
|
} else |
|
scr_LogPrint(LPRINT_LOGNORM, "Server does not support bookmarks storage."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
ansqry = lm_message_node_get_child(m->node, "query"); |
|
ansqry = lm_message_node_get_child(ansqry, "storage"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! (storage:bookmarks)"); |
|
return 0; |
|
} |
|
|
|
// Walk through the storage tags |
|
for (x = ansqry->children ; x; x = x->next) { |
|
// If the current node is a conference item, parse it and update the roster |
|
if (x->name && !strcmp(x->name, "conference")) |
|
storage_bookmarks_parse_conference(x); |
|
} |
|
// "Copy" the bookmarks node |
|
if (bookmarks) |
|
lm_message_node_unref(bookmarks); |
|
lm_message_node_deep_ref(ansqry); |
|
bookmarks = ansqry; |
|
return 0; |
|
} |
|
|
|
|
|
static LmHandlerResult cb_storage_rosternotes(LmMessageHandler *h, |
|
LmConnection *c, |
|
LmMessage *m, gpointer user_data) |
|
{ |
|
LmMessageNode *ansqry; |
|
|
|
if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { |
|
const char *p = NULL; |
|
LmMessageNode *error = lm_message_node_get_child(m->node, "error"); |
|
// No server support, or no roster notes? |
|
if (error && error->children) |
|
p = error->children->name; |
|
if (p && !strcmp(p, "item-not-found")) { |
|
// item-no-found means the server has Private Storage, but it's |
|
// currently empty. |
|
if (rosternotes) |
|
lm_message_node_unref(rosternotes); |
|
rosternotes = lm_message_node_new("storage", "storage:rosternotes"); |
|
// We return 0 so that the IQ error message be |
|
// not displayed, as it isn't a real error. |
|
} else |
|
scr_LogPrint(LPRINT_LOGNORM, "Server does not support roster notes storage."); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
|
|
ansqry = lm_message_node_get_child(m->node, "query"); |
|
ansqry = lm_message_node_get_child(ansqry, "storage"); |
|
if (!ansqry) { |
|
scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! " |
|
"(storage:rosternotes)"); |
|
return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
} |
|
// Copy the rosternotes node |
|
if (rosternotes) |
|
lm_message_node_unref(rosternotes); |
|
lm_message_node_deep_ref(ansqry); |
|
rosternotes = ansqry; |
|
return 0; |
|
} |
|
|
|
|
|
static struct IqRequestStorageHandlers |
|
{ |
|
const gchar *storagens; |
|
LmHandleMessageFunction handler; |
|
} iq_request_storage_handlers[] = { |
|
{"storage:rosternotes", &cb_storage_rosternotes}, |
|
{"storage:bookmarks", &cb_storage_bookmarks}, |
|
{NULL, NULL} |
|
}; |
|
|
|
void xmpp_request_storage(const gchar *storage) |
|
{ |
|
LmMessage *iq; |
|
LmMessageNode *query; |
|
LmMessageHandler *handler; |
|
int i; |
|
|
|
iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ, |
|
LM_MESSAGE_SUB_TYPE_GET); |
|
query = lm_message_node_add_child(iq->node, "query", NULL); |
|
lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE); |
|
lm_message_node_set_attribute(lm_message_node_add_child |
|
(query, "storage", NULL), |
|
"xmlns", storage); |
|
|
|
for (i = 0; |
|
strcmp(iq_request_storage_handlers[i].storagens, storage) != 0; |
|
++i) ; |
|
|
|
handler = lm_message_handler_new(iq_request_storage_handlers[i].handler, |
|
NULL, FALSE); |
|
lm_connection_send_with_reply(lconnection, iq, handler, NULL); |
|
lm_message_handler_unref(handler); |
|
lm_message_unref(iq); |
|
} |
|
|
|
/* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */
|
|
|