changeset 17598:43df07968000

Implemented XEP-0050: Ad-Hoc Commands. Note that this XEP requires sending an initial command to the peer, which is not implemented in libpurple itself (since this requires a discovery browser or equivalent).
author Andreas Monitzer <pidgin@monitzer.com>
date Tue, 19 Jun 2007 02:53:24 +0000
parents 5f4dcaf1f886
children 8c3fbc353a9c
files libpurple/protocols/jabber/adhoccommands.c libpurple/protocols/jabber/adhoccommands.h libpurple/protocols/jabber/iq.c libpurple/protocols/jabber/xdata.c libpurple/protocols/jabber/xdata.h
diffstat 5 files changed, 241 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Tue Jun 19 02:53:24 2007 +0000
@@ -0,0 +1,130 @@
+/*
+ * purple - Jabber Protocol Plugin
+ *
+ * Copyright (C) 2007, Andreas Monitzer <andy@monitzer.com>
+ *
+ * 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 "adhoccommands.h"
+#include <assert.h>
+#include <string.h>
+#include "internal.h"
+#include "xdata.h"
+#include "iq.h"
+#include "request.h"
+
+static void do_adhoc_ignoreme(JabberStream *js, ...) {
+	/* we don't have to do anything */
+}
+
+typedef struct _JabberAdHocActionInfo {
+	char *sessionid;
+	char *who;
+	char *node;
+	GList *actionslist;
+} JabberAdHocActionInfo;
+
+/*static void do_adhoc_parse_iq(JabberStream *js, xmlnode *packet, gpointer data) {
+	jabber_adhoc_parse(js, packet);
+}*/
+
+static void do_adhoc_action_cb(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data) {
+	xmlnode *command;
+	GList *action;
+	JabberAdHocActionInfo *actionInfo = user_data;
+	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
+/*	jabber_iq_set_callback(iq, do_adhoc_parse_iq, NULL);*/
+	
+	xmlnode_set_attrib(iq->node, "to", actionInfo->who);
+	command = xmlnode_new_child(iq->node,"command");
+	xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
+	xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid);
+	xmlnode_set_attrib(command,"node",actionInfo->node);
+	if(actionhandle)
+		xmlnode_set_attrib(command,"action",actionhandle);
+	xmlnode_insert_child(command,result);
+	
+	for(action = actionInfo->actionslist; action; action = g_list_next(action)) {
+		char *handle = action->data;
+		g_free(handle);
+	}
+	g_list_free(actionInfo->actionslist);
+	g_free(actionInfo->sessionid);
+	g_free(actionInfo->who);
+	g_free(actionInfo->node);
+	
+	jabber_iq_send(iq);
+}
+
+void jabber_adhoc_parse(JabberStream *js, xmlnode *packet) {
+	xmlnode *command = xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands");
+	const char *status = xmlnode_get_attrib(command,"status");
+	xmlnode *xdata = xmlnode_get_child_with_namespace(command,"x","jabber:x:data");
+	
+	if(!status)
+		return;
+	
+	if(!strcmp(status,"completed")) {
+		/* display result */
+		xmlnode *note = xmlnode_get_child(command,"note");
+		
+		if(note)
+			purple_request_action(js, xmlnode_get_attrib(packet, "from"), xmlnode_get_data(note), NULL, 0, purple_connection_get_account(js->gc), xmlnode_get_attrib(packet, "from"), NULL, NULL, 1, _("OK"), do_adhoc_ignoreme);
+		
+		if(xdata)
+			jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL);
+		return;
+	}
+	if(!strcmp(status,"executing")) {
+		/* this command needs more steps */
+		xmlnode *actions, *action;
+		int actionindex = 0;
+		GList *actionslist = NULL;
+		JabberAdHocActionInfo *actionInfo;
+		if(!xdata)
+			return; /* shouldn't happen */
+		
+		actions = xmlnode_get_child(command,"actions");
+		if(!actions) {
+			JabberXDataAction *defaultaction = g_new0(JabberXDataAction, 1);
+			defaultaction->name = g_strdup(_("execute"));
+			defaultaction->handle = g_strdup("execute");
+			actionslist = g_list_append(actionslist, defaultaction);
+		} else {
+			const char *defaultactionhandle = xmlnode_get_attrib(actions, "execute");
+			int index = 0;
+			for(action = actions->child; action; action = action->next, ++index) {
+				if(action->type == XMLNODE_TYPE_TAG) {
+					JabberXDataAction *newaction = g_new0(JabberXDataAction, 1);
+					newaction->name = g_strdup(_(action->name));
+					newaction->handle = g_strdup(action->name);
+					actionslist = g_list_append(actionslist, newaction);
+					if(defaultactionhandle && !strcmp(defaultactionhandle, action->name))
+						actionindex = index;
+				}
+			}
+		}
+		
+		actionInfo = g_new0(JabberAdHocActionInfo, 1);
+		actionInfo->sessionid = g_strdup(xmlnode_get_attrib(command,"sessionid"));
+		actionInfo->who = g_strdup(xmlnode_get_attrib(packet,"from"));
+		actionInfo->node = g_strdup(xmlnode_get_attrib(command,"node"));
+		actionInfo->actionslist = actionslist;
+		
+		jabber_x_data_request_with_actions(js,xdata,actionslist,actionindex,do_adhoc_action_cb,actionInfo);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.h	Tue Jun 19 02:53:24 2007 +0000
@@ -0,0 +1,31 @@
+/*
+ * purple - Jabber Protocol Plugin
+ *
+ * Copyright (C) 2007, Andreas Monitzer <andy@monitzer.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef _PURPLE_JABBER_ADHOCCOMMANDS_H_
+#define _PURPLE_JABBER_ADHOCCOMMANDS_H_
+
+#include "jabber.h"
+
+/* Implementation of XEP-0050 */
+
+void jabber_adhoc_parse(JabberStream *js, xmlnode *packet);
+
+#endif /* _PURPLE_JABBER_ADHOCCOMMANDS_H_ */
--- a/libpurple/protocols/jabber/iq.c	Mon Jun 18 15:05:39 2007 +0000
+++ b/libpurple/protocols/jabber/iq.c	Tue Jun 19 02:53:24 2007 +0000
@@ -31,6 +31,7 @@
 #include "roster.h"
 #include "si.h"
 #include "ping.h"
+#include "adhoccommands.h"
 
 #ifdef _WIN32
 #include "utsname.h"
@@ -290,6 +291,11 @@
 		jabber_gmail_poke(js, packet);
 		return;
 	}
+	
+	if(xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands")) {
+		jabber_adhoc_parse(js, packet);
+		return;
+	}
 
 	purple_debug_info("jabber", "jabber_iq_parse\n");
 
--- a/libpurple/protocols/jabber/xdata.c	Mon Jun 18 15:05:39 2007 +0000
+++ b/libpurple/protocols/jabber/xdata.c	Tue Jun 19 02:53:24 2007 +0000
@@ -37,22 +37,39 @@
 struct jabber_x_data_data {
 	GHashTable *fields;
 	GSList *values;
-	jabber_x_data_cb cb;
+	jabber_x_data_action_cb cb;
 	gpointer user_data;
 	JabberStream *js;
+	GList *actions;
+	PurpleRequestFieldGroup *actiongroup;
 };
 
 static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
 	xmlnode *result = xmlnode_new("x");
-	jabber_x_data_cb cb = data->cb;
+	jabber_x_data_action_cb cb = data->cb;
 	gpointer user_data = data->user_data;
 	JabberStream *js = data->js;
 	GList *groups, *flds;
+	char *actionhandle = NULL;
+	gboolean hasActions = (data->actions != NULL);
 
 	xmlnode_set_namespace(result, "jabber:x:data");
 	xmlnode_set_attrib(result, "type", "submit");
 
 	for(groups = purple_request_fields_get_groups(fields); groups; groups = groups->next) {
+		if(groups->data == data->actiongroup) {
+			for(flds = purple_request_field_group_get_fields(groups->data); flds; flds = flds->next) {
+				PurpleRequestField *field = flds->data;
+				const char *id = purple_request_field_get_id(field);
+				int handleindex;
+				if(strcmp(id, "libpurple:jabber:xdata:actions"))
+					continue;
+				handleindex = purple_request_field_choice_get_value(field);
+				actionhandle = g_strdup(g_list_nth_data(data->actions, handleindex));
+				break;
+			}
+			continue;
+		}
 		for(flds = purple_request_field_group_get_fields(groups->data); flds; flds = flds->next) {
 			xmlnode *fieldnode, *valuenode;
 			PurpleRequestField *field = flds->data;
@@ -127,31 +144,59 @@
 		g_free(data->values->data);
 		data->values = g_slist_delete_link(data->values, data->values);
 	}
+	if (data->actions) {
+		GList *action;
+		for(action = data->actions; action; action = g_list_next(action)) {
+			g_free(action->data);
+		}
+		g_list_free(data->actions);
+	}
 	g_free(data);
 
-	cb(js, result, user_data);
+	if (hasActions) {
+		cb(js, result, actionhandle, user_data);
+		g_free(actionhandle);
+	} else
+		((jabber_x_data_cb)cb)(js, result, user_data);
 }
 
 static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
 	xmlnode *result = xmlnode_new("x");
-	jabber_x_data_cb cb = data->cb;
+	jabber_x_data_action_cb cb = data->cb;
 	gpointer user_data = data->user_data;
 	JabberStream *js = data->js;
+	gboolean hasActions = FALSE;
 	g_hash_table_destroy(data->fields);
 	while(data->values) {
 		g_free(data->values->data);
 		data->values = g_slist_delete_link(data->values, data->values);
 	}
+	if (data->actions) {
+		hasActions = TRUE;
+		GList *action;
+		for(action = data->actions; action; action = g_list_next(action)) {
+			g_free(action->data);
+		}
+		g_list_free(data->actions);
+	}
 	g_free(data);
 
 	xmlnode_set_namespace(result, "jabber:x:data");
 	xmlnode_set_attrib(result, "type", "cancel");
 
-	cb(js, result, user_data);
+	if (hasActions)
+		cb(js, result, NULL, user_data);
+	else
+		((jabber_x_data_cb)cb)(js, result, user_data);
 }
 
 void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data)
 {
+	return jabber_x_data_request_with_actions(js, packet, NULL, 0, (jabber_x_data_action_cb)cb, user_data);
+}
+
+void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
+{
 	void *handle;
 	xmlnode *fn, *x;
 	PurpleRequestFields *fields;
@@ -326,6 +371,23 @@
         if(field && xmlnode_get_child(fn, "required"))
             purple_request_field_set_required(field,TRUE);
 	}
+	
+	if(actions != NULL) {
+		PurpleRequestField *actionfield;
+		GList *action;
+		data->actiongroup = group = purple_request_field_group_new(_("Actions"));
+		purple_request_fields_add_group(fields, group);
+		actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), defaultaction);
+
+		for(action = actions; action; action = g_list_next(action)) {
+			JabberXDataAction *a = action->data;
+			
+			purple_request_field_choice_add(actionfield, a->name);
+			data->actions = g_list_append(data->actions, g_strdup(a->handle));
+		}
+		purple_request_field_set_required(actionfield,TRUE);
+		purple_request_field_group_add_field(group, actionfield);
+	}
 
 	if((x = xmlnode_get_child(packet, "title")))
 		title = xmlnode_get_data(x);
--- a/libpurple/protocols/jabber/xdata.h	Mon Jun 18 15:05:39 2007 +0000
+++ b/libpurple/protocols/jabber/xdata.h	Tue Jun 19 02:53:24 2007 +0000
@@ -25,7 +25,14 @@
 #include "jabber.h"
 #include "xmlnode.h"
 
+typedef struct _JabberXDataAction {
+	char *name;
+	char *handle;
+} JabberXDataAction;
+
 typedef void (*jabber_x_data_cb)(JabberStream *js, xmlnode *result, gpointer user_data);
+typedef void (*jabber_x_data_action_cb)(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data);
 void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data);
+void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data);
 
 #endif /* _PURPLE_JABBER_XDATA_H_ */