/*
* disco.c -- Service discovery requests
*
* Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.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 <glib.h>
#include <gmodule.h>
#include <loudmouth/loudmouth.h>
#include <string.h>
#include "commands.h"
#include "logprint.h"
#include "utils.h"
#include "xmpp.h"
#include "compl.h"
#include "xmpp_defines.h"
#include "screen.h"
#include "hbuf.h"
static guint disco_cid = 0;
static LmMessageHandler *disco_info_reply_handler = NULL;
static LmMessageHandler *disco_items_reply_handler = NULL;
static LmHandlerResult disco_handler (LmMessage *message, gboolean info_request)
{
switch (lm_message_get_sub_type (message)) {
case LM_MESSAGE_SUB_TYPE_RESULT:
{
LmMessageNode *node = lm_message_get_node (message);
const gchar *from = lm_message_node_get_attribute (node, "from");
GString *info;
node = lm_message_node_get_child (node, "query");
// check xmlns
if (!node || strcmp (lm_message_node_get_attribute (node, "xmlns"), info_request ? NS_DISCO_INFO : NS_DISCO_ITEMS))
break;
{ // header for user message
const gchar *rnode = lm_message_node_get_attribute (node, "node");
// create user message string
info = g_string_new (NULL);
g_string_printf (info, "Service discovery %s results for %s", info_request ? "info" : "items", from);
if (rnode)
g_string_append_printf (info, " (%s):", rnode);
else
g_string_append (info, ":");
}
if (node->children) {
// parse request results
if (info_request) { // info
GString *identities = g_string_new (NULL);
GString *features = g_string_new (NULL);
for (node = node->children; node; node = node->next) {
if (!strcasecmp (node->name, "identity")) {
const gchar *category = lm_message_node_get_attribute (node, "category");
const gchar *type = lm_message_node_get_attribute (node, "type");
const gchar *name = lm_message_node_get_attribute (node, "name");
g_string_append_printf (identities, "\n [%s (%s)] %s", category ? category : "none", type ? type : "none", name ? name : "");
} else if (!strcasecmp (node->name, "feature")) {
const gchar *var = lm_message_node_get_attribute (node, "var");
g_string_append_printf (features, "\n [%s]", var ? var : "none");
}
}
if (identities->len)
g_string_append_printf (info, "\n Identities:%s", identities->str);
if (features->len)
g_string_append_printf (info, "\n Features:%s", features->str);
g_string_free (identities, TRUE);
g_string_free (features, TRUE);
} else { // items
for (node = node->children; node; node = node->next) {
const gchar *name = lm_message_node_get_attribute (node, "name");
const gchar *jid = lm_message_node_get_attribute (node, "jid");
const gchar *inode = lm_message_node_get_attribute (node, "node");
if (inode)
g_string_append_printf (info, "\n [%s (%s)] %s", jid ? jid : "none", inode, name ? name : "");
else
g_string_append_printf (info, "\n [%s] %s", jid ? jid : "none", name ? name : "");
}
}
} else
g_string_append (info, "\n Empty result.");
{ // print to buddy's buffer
gchar *jid = jidtodisp (from);
// XXX check for message size? conference server lists may be huge...
scr_WriteIncomingMessage (jid, info->str, 0, HBB_PREFIX_INFO, 0); // NO conversion from utf-8
g_free (jid);
}
g_string_free (info, TRUE);
}
break;
case LM_MESSAGE_SUB_TYPE_ERROR:
{
LmMessageNode *node = lm_message_get_node (message);
const gchar *from = lm_message_node_get_attribute (node, "from");
const gchar *type;
const gchar *reason;
node = lm_message_node_get_child (node, "error");
type = lm_message_node_get_attribute (node, "type");
if (node->children)
reason = node->children->name;
else
reason = "undefined";
{ // print to buddy's buffer
gchar *jid = jidtodisp (from);
gchar *mesg = g_strdup_printf ("Service %s discovery for %s failed: %s - %s", info_request ? "info" : "items", from, type, reason);
scr_WriteIncomingMessage (jid, mesg, 0, HBB_PREFIX_INFO, 0);
g_free (mesg);
g_free (jid);
}
}
break;
default:
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
break;
}
return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}
static LmHandlerResult disco_info_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata)
{
return disco_handler (message, TRUE);
}
static LmHandlerResult disco_items_handler (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer udata)
{
return disco_handler (message, FALSE);
}
static void do_disco (char *arg)
{
char **args = split_arg (arg, 3, 0);
int info = -1;
if (!args[0] || !strcmp (args[0], "info"))
info = 1;
else if (!strcmp (args[0], "items"))
info = 0;
else
scr_LogPrint (LPRINT_NORMAL, "Unknown subcomand.");
if (info != -1) {
LmMessageHandler *handler;
LmMessage *request;
char *to = NULL;
char *dnode = NULL;
if (args[0] && args[1]) {
if (*args[1] == '.') {
if (*(args[1] + 1) == JID_RESOURCE_SEPARATOR) { // allow "./resource" notation
char *rest = to_utf8 (args[1] + 1);
to = g_strdup_printf ("%s%s", CURRENT_JID, rest);
g_free (rest);
}
} else
to = to_utf8 (args[1]);
if (args[2])
dnode = to_utf8 (args[2]);
}
// XXX send to all resources/current resource?
{ // create message
LmMessageNode *node;
request = lm_message_new_with_sub_type (to ? to : CURRENT_JID, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
node = lm_message_get_node (request);
node = lm_message_node_add_child (node, "query", NULL);
lm_message_node_set_attribute (node, "xmlns", info ? NS_DISCO_INFO : NS_DISCO_ITEMS);
if (dnode)
lm_message_node_set_attribute (node, "node", dnode);
}
lm_connection_send_with_reply (lconnection, request, info ? disco_info_reply_handler : disco_items_reply_handler, NULL);
lm_message_unref (request);
if (dnode)
g_free (dnode);
g_free (to);
}
free_arg_lst (args);
}
const gchar *g_module_check_init(GModule *module)
{
// create handlers
disco_info_reply_handler = lm_message_handler_new (disco_info_handler, NULL, NULL);
disco_items_reply_handler = lm_message_handler_new (disco_items_handler, NULL, NULL);
// completion
disco_cid = compl_new_category ();
if (disco_cid) {
compl_add_category_word (disco_cid, "info");
compl_add_category_word (disco_cid, "items");
}
// command
cmd_add ("disco", "", disco_cid, COMPL_JID, do_disco, NULL);
return NULL;
}
void g_module_unload(GModule *module)
{
// command
cmd_del ("disco");
// completion
if (disco_cid)
compl_del_category (disco_cid);
// unregister handlers
if (disco_info_reply_handler) {
lm_message_handler_invalidate (disco_info_reply_handler);
lm_message_handler_unref (disco_info_reply_handler);
}
if (disco_items_reply_handler) {
lm_message_handler_invalidate (disco_items_reply_handler);
lm_message_handler_unref (disco_items_reply_handler);
}
}
/* vim: se ts=4 sw=4: */