Mercurial > hg
diff mcabber/mcabber/xmpp_iqrequest.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_iqrequest.c@351427ef0b4b |
children | b09f82f61745 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mcabber/mcabber/xmpp_iqrequest.c Mon Jan 18 15:36:19 2010 +0200 @@ -0,0 +1,636 @@ +/* + * xmpp_iqrequest.c -- Jabber IQ request handling + * + * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de> + * Copyright (C) 2005-2009 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <string.h> +#include <stdlib.h> + +#include "xmpp_helper.h" +#include "xmpp_iq.h" +#include "screen.h" +#include "utils.h" +#include "settings.h" +#include "hooks.h" +#include "hbuf.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_vcard(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_TIME, "query", &cb_time}, + {NS_LAST, "query", &cb_last}, + {NS_VCARD, "vCard", &cb_vcard}, + {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, +}; + +// 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; + int i; + + iq = lm_message_new_with_sub_type(fulljid, LM_MESSAGE_TYPE_IQ, + LM_MESSAGE_SUB_TYPE_GET); + for (i = 0; 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); + handler = lm_message_handler_new(iq_request_handlers[i].handler, + NULL, FALSE); + lm_connection_send_with_reply(lconnection, iq, handler, NULL); + lm_message_handler_unref(handler); + lm_message_unref(iq); +} + +// 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_find_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 + hook_execute_internal("hook-post-connect"); + + 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 *tmp; + char *buf; + + ansqry = lm_message_node_get_child(m->node, "query"); + if (!ansqry) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result!"); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + + // Display IQ result sender... + p = lm_message_get_from(m); + if (!p) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name)."); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + bjid = p; + + 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 + tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); + if (tmp) *tmp = '\0'; + + scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); + g_free(buf); + + // Get result data... + p = lm_message_node_get_child_value(ansqry, "name"); + if (p) { + buf = g_strdup_printf("Name: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + p = lm_message_node_get_child_value(ansqry, "version"); + if (p) { + buf = g_strdup_printf("Version: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + p = lm_message_node_get_child_value(ansqry, "os"); + if (p) { + buf = g_strdup_printf("OS: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + 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 *tmp; + char *buf; + + ansqry = lm_message_node_get_child(m->node, "query"); + if (!ansqry) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result!"); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + // Display IQ result sender... + p = lm_message_get_from(m); + if (!p) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result (no sender name)."); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + bjid = p; + + 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 + tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); + if (tmp) *tmp = '\0'; + + scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); + g_free(buf); + + // Get result data... + p = lm_message_node_get_child_value(ansqry, "utc"); + if (p) { + buf = g_strdup_printf("UTC: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + p = lm_message_node_get_child_value(ansqry, "tz"); + if (p) { + buf = g_strdup_printf("TZ: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + p = lm_message_node_get_child_value(ansqry, "display"); + if (p) { + buf = g_strdup_printf("Time: %s", p); + scr_WriteIncomingMessage(bjid, buf, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_free(buf); + } + 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 *buf, *tmp; + + ansqry = lm_message_node_get_child(m->node, "query"); + if (!ansqry) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result!"); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + // Display IQ result sender... + p = lm_message_get_from(m); + if (!p) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result (no sender name)."); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + bjid = p; + + 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 + tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); + if (tmp) *tmp = '\0'; + + scr_WriteIncomingMessage(bjid, 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(bjid, sbuf->str, + 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); + g_string_free(sbuf, TRUE); + } else { + scr_WriteIncomingMessage(bjid, "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(bjid, buf, 0, HBB_PREFIX_INFO, 0); + g_free(buf); + } + 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 || !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; + data = lm_message_node_get_value(x); + if (!p || !data) + continue; + + 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 *p, *bjid; + char *buf, *tmp; + + // Display IQ result sender... + p = lm_message_get_from(m); + if (!p) { + scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:vCard result (no sender name)."); + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + } + bjid = p; + + 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 + tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); + if (tmp) *tmp = '\0'; + + scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); + g_free(buf); + + // Get result data... + handle_vcard_node(bjid, ansqry); + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +static void storage_bookmarks_parse_conference(LmMessageNode *node) +{ + const char *fjid, *name, *autojoin; + const char *pstatus, *awhois; + 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"); + + 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, NULL, 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); + */ + } + + // 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")) + i = autowhois_on; + else if (!strcmp(awhois, "0")) + i = autowhois_off; + if (i != autowhois_default) + buddy_setautowhois(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")) { + 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); +} + +static LmHandlerResult cb_storage_bookmarks(LmMessageHandler *h, + LmConnection *c, + LmMessage *m, gpointer user_data) +{ + LmMessageNode *x, *ansqry; + char *p; + + if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { + // No server support, or no bookmarks? + p = m->node->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. + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error + } + + 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; + // No server support, or no roster notes? + p = m->node->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. + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } + return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error + } + + 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 expandtab cindent cinoptions=>2\:2(0: For Vim users... */