/src/pidgin/libpurple/protocols/jabber/adhoccommands.c
Line | Count | Source |
1 | | /* |
2 | | * purple - Jabber Protocol Plugin |
3 | | * |
4 | | * Purple is the legal property of its developers, whose names are too numerous |
5 | | * to list here. Please refer to the COPYRIGHT file distributed with this |
6 | | * source distribution. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
21 | | * |
22 | | */ |
23 | | |
24 | | #include "internal.h" |
25 | | |
26 | | #include "adhoccommands.h" |
27 | | #include <string.h> |
28 | | #include "internal.h" |
29 | | #include "xdata.h" |
30 | | #include "iq.h" |
31 | | #include "request.h" |
32 | | |
33 | 0 | static void do_adhoc_ignoreme(JabberStream *js, ...) { |
34 | | /* we don't have to do anything */ |
35 | 0 | } |
36 | | |
37 | | typedef struct _JabberAdHocActionInfo { |
38 | | char *sessionid; |
39 | | char *who; |
40 | | char *node; |
41 | | GList *actionslist; |
42 | | } JabberAdHocActionInfo; |
43 | | |
44 | | static void |
45 | | jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, xmlnode *query) |
46 | 0 | { |
47 | 0 | JabberID *jid; |
48 | 0 | JabberBuddy *jb; |
49 | 0 | JabberBuddyResource *jbr = NULL; |
50 | 0 | xmlnode *item; |
51 | |
|
52 | 0 | if ((jid = jabber_id_new(from))) { |
53 | 0 | if (jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) |
54 | 0 | jbr = jabber_buddy_find_resource(jb, jid->resource); |
55 | 0 | jabber_id_free(jid); |
56 | 0 | } |
57 | |
|
58 | 0 | if(!jbr) |
59 | 0 | return; |
60 | | |
61 | 0 | if(jbr->commands) { |
62 | | /* since the list we just received is complete, wipe the old one */ |
63 | 0 | while(jbr->commands) { |
64 | 0 | JabberAdHocCommands *cmd = jbr->commands->data; |
65 | 0 | g_free(cmd->jid); |
66 | 0 | g_free(cmd->node); |
67 | 0 | g_free(cmd->name); |
68 | 0 | g_free(cmd); |
69 | 0 | jbr->commands = g_list_delete_link(jbr->commands, jbr->commands); |
70 | 0 | } |
71 | 0 | } |
72 | |
|
73 | 0 | for(item = query->child; item; item = item->next) { |
74 | 0 | JabberAdHocCommands *cmd; |
75 | 0 | if(item->type != XMLNODE_TYPE_TAG) |
76 | 0 | continue; |
77 | 0 | if(!purple_strequal(item->name, "item")) |
78 | 0 | continue; |
79 | 0 | cmd = g_new0(JabberAdHocCommands, 1); |
80 | |
|
81 | 0 | cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid")); |
82 | 0 | cmd->node = g_strdup(xmlnode_get_attrib(item,"node")); |
83 | 0 | cmd->name = g_strdup(xmlnode_get_attrib(item,"name")); |
84 | |
|
85 | 0 | jbr->commands = g_list_append(jbr->commands,cmd); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | void |
90 | | jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, |
91 | | JabberIqType type, const char *id, |
92 | | xmlnode *packet, gpointer data) |
93 | 0 | { |
94 | 0 | xmlnode *query; |
95 | 0 | const char *node; |
96 | |
|
97 | 0 | if (type == JABBER_IQ_ERROR) |
98 | 0 | return; |
99 | | |
100 | 0 | query = xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_ITEMS); |
101 | 0 | if (!query) |
102 | 0 | return; |
103 | 0 | node = xmlnode_get_attrib(query, "node"); |
104 | 0 | if (!purple_strequal(node, "http://jabber.org/protocol/commands")) |
105 | 0 | return; |
106 | | |
107 | 0 | jabber_adhoc_got_buddy_list(js, from, query); |
108 | 0 | } |
109 | | |
110 | | static void jabber_adhoc_parse(JabberStream *js, const char *from, |
111 | | JabberIqType type, const char *id, |
112 | | xmlnode *packet, gpointer data); |
113 | | |
114 | 0 | static void do_adhoc_action_cb(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data) { |
115 | 0 | xmlnode *command; |
116 | 0 | GList *action; |
117 | 0 | JabberAdHocActionInfo *actionInfo = user_data; |
118 | 0 | JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); |
119 | 0 | jabber_iq_set_callback(iq, jabber_adhoc_parse, NULL); |
120 | |
|
121 | 0 | xmlnode_set_attrib(iq->node, "to", actionInfo->who); |
122 | 0 | command = xmlnode_new_child(iq->node,"command"); |
123 | 0 | xmlnode_set_namespace(command,"http://jabber.org/protocol/commands"); |
124 | 0 | xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid); |
125 | 0 | xmlnode_set_attrib(command,"node",actionInfo->node); |
126 | | |
127 | | /* cancel is handled differently on ad-hoc commands than regular forms */ |
128 | 0 | if (purple_strequal(xmlnode_get_namespace(result), "jabber:x:data") && |
129 | 0 | purple_strequal(xmlnode_get_attrib(result, "type"), "cancel")) { |
130 | 0 | xmlnode_set_attrib(command,"action","cancel"); |
131 | 0 | } else { |
132 | 0 | if(actionhandle) |
133 | 0 | xmlnode_set_attrib(command,"action",actionhandle); |
134 | 0 | xmlnode_insert_child(command,result); |
135 | 0 | } |
136 | |
|
137 | 0 | for(action = actionInfo->actionslist; action; action = g_list_next(action)) { |
138 | 0 | char *handle = action->data; |
139 | 0 | g_free(handle); |
140 | 0 | } |
141 | 0 | g_list_free(actionInfo->actionslist); |
142 | 0 | g_free(actionInfo->sessionid); |
143 | 0 | g_free(actionInfo->who); |
144 | 0 | g_free(actionInfo->node); |
145 | |
|
146 | 0 | jabber_iq_send(iq); |
147 | 0 | } |
148 | | |
149 | | static void |
150 | | jabber_adhoc_parse(JabberStream *js, const char *from, |
151 | | JabberIqType type, const char *id, |
152 | | xmlnode *packet, gpointer data) |
153 | 0 | { |
154 | 0 | xmlnode *command = xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands"); |
155 | 0 | const char *status = xmlnode_get_attrib(command,"status"); |
156 | 0 | xmlnode *xdata = xmlnode_get_child_with_namespace(command,"x","jabber:x:data"); |
157 | |
|
158 | 0 | if (type == JABBER_IQ_ERROR) { |
159 | 0 | char *msg = jabber_parse_error(js, packet, NULL); |
160 | 0 | if(!msg) |
161 | 0 | msg = g_strdup(_("Unknown Error")); |
162 | |
|
163 | 0 | purple_notify_error(NULL, _("Ad-Hoc Command Failed"), |
164 | 0 | _("Ad-Hoc Command Failed"), msg); |
165 | 0 | g_free(msg); |
166 | 0 | return; |
167 | 0 | } |
168 | | |
169 | 0 | if(!status) |
170 | 0 | return; |
171 | | |
172 | 0 | if(purple_strequal(status,"completed")) { |
173 | | /* display result */ |
174 | 0 | xmlnode *note = xmlnode_get_child(command,"note"); |
175 | |
|
176 | 0 | if(note) { |
177 | 0 | char *data = xmlnode_get_data(note); |
178 | 0 | purple_notify_info(NULL, from, data, NULL); |
179 | 0 | g_free(data); |
180 | 0 | } |
181 | |
|
182 | 0 | if(xdata) |
183 | 0 | jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL); |
184 | 0 | return; |
185 | 0 | } |
186 | 0 | if(purple_strequal(status,"executing")) { |
187 | | /* this command needs more steps */ |
188 | 0 | xmlnode *actions, *action; |
189 | 0 | int actionindex = 0; |
190 | 0 | GList *actionslist = NULL; |
191 | 0 | JabberAdHocActionInfo *actionInfo; |
192 | 0 | if(!xdata) |
193 | 0 | return; /* shouldn't happen */ |
194 | | |
195 | 0 | actions = xmlnode_get_child(command,"actions"); |
196 | 0 | if(!actions) { |
197 | 0 | JabberXDataAction *defaultaction = g_new0(JabberXDataAction, 1); |
198 | 0 | defaultaction->name = g_strdup(_("execute")); |
199 | 0 | defaultaction->handle = g_strdup("execute"); |
200 | 0 | actionslist = g_list_append(actionslist, defaultaction); |
201 | 0 | } else { |
202 | 0 | const char *defaultactionhandle = xmlnode_get_attrib(actions, "execute"); |
203 | 0 | int index = 0; |
204 | 0 | for(action = actions->child; action; action = action->next, ++index) { |
205 | 0 | if(action->type == XMLNODE_TYPE_TAG) { |
206 | 0 | JabberXDataAction *newaction = g_new0(JabberXDataAction, 1); |
207 | 0 | newaction->name = g_strdup(_(action->name)); |
208 | 0 | newaction->handle = g_strdup(action->name); |
209 | 0 | actionslist = g_list_append(actionslist, newaction); |
210 | 0 | if(defaultactionhandle && purple_strequal(defaultactionhandle, action->name)) |
211 | 0 | actionindex = index; |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | |
|
216 | 0 | actionInfo = g_new0(JabberAdHocActionInfo, 1); |
217 | 0 | actionInfo->sessionid = g_strdup(xmlnode_get_attrib(command,"sessionid")); |
218 | 0 | actionInfo->who = g_strdup(from); |
219 | 0 | actionInfo->node = g_strdup(xmlnode_get_attrib(command,"node")); |
220 | 0 | actionInfo->actionslist = actionslist; |
221 | |
|
222 | 0 | jabber_x_data_request_with_actions(js,xdata,actionslist,actionindex,do_adhoc_action_cb,actionInfo); |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | 0 | void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data) { |
227 | 0 | if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
228 | 0 | JabberAdHocCommands *cmd = data; |
229 | 0 | PurpleBuddy *buddy = (PurpleBuddy *) node; |
230 | 0 | PurpleAccount *account = purple_buddy_get_account(buddy); |
231 | 0 | JabberStream *js = purple_account_get_connection(account)->proto_data; |
232 | |
|
233 | 0 | jabber_adhoc_execute(js, cmd); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | static void |
238 | | jabber_adhoc_got_server_list(JabberStream *js, const char *from, xmlnode *query) |
239 | 0 | { |
240 | 0 | xmlnode *item; |
241 | |
|
242 | 0 | if(!query) |
243 | 0 | return; |
244 | | |
245 | | /* clean current list (just in case there is one) */ |
246 | 0 | while(js->commands) { |
247 | 0 | JabberAdHocCommands *cmd = js->commands->data; |
248 | 0 | g_free(cmd->jid); |
249 | 0 | g_free(cmd->node); |
250 | 0 | g_free(cmd->name); |
251 | 0 | g_free(cmd); |
252 | 0 | js->commands = g_list_delete_link(js->commands, js->commands); |
253 | 0 | } |
254 | | |
255 | | /* re-fill list */ |
256 | 0 | for(item = query->child; item; item = item->next) { |
257 | 0 | JabberAdHocCommands *cmd; |
258 | 0 | if(item->type != XMLNODE_TYPE_TAG) |
259 | 0 | continue; |
260 | 0 | if(!purple_strequal(item->name, "item")) |
261 | 0 | continue; |
262 | 0 | cmd = g_new0(JabberAdHocCommands, 1); |
263 | 0 | cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid")); |
264 | 0 | cmd->node = g_strdup(xmlnode_get_attrib(item,"node")); |
265 | 0 | cmd->name = g_strdup(xmlnode_get_attrib(item,"name")); |
266 | |
|
267 | 0 | js->commands = g_list_append(js->commands,cmd); |
268 | 0 | } |
269 | |
|
270 | 0 | if (js->state == JABBER_STREAM_CONNECTED) |
271 | 0 | purple_prpl_got_account_actions(purple_connection_get_account(js->gc)); |
272 | 0 | } |
273 | | |
274 | | static void |
275 | | jabber_adhoc_server_got_list_cb(JabberStream *js, const char *from, |
276 | | JabberIqType type, const char *id, |
277 | | xmlnode *packet, gpointer data) |
278 | 0 | { |
279 | 0 | xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", |
280 | 0 | NS_DISCO_ITEMS); |
281 | |
|
282 | 0 | jabber_adhoc_got_server_list(js, from, query); |
283 | |
|
284 | 0 | } |
285 | | |
286 | | void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query) |
287 | 0 | { |
288 | 0 | if (purple_strequal(from, js->user->domain)) { |
289 | 0 | jabber_adhoc_got_server_list(js, from, query); |
290 | 0 | } else { |
291 | 0 | jabber_adhoc_got_buddy_list(js, from, query); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | 0 | void jabber_adhoc_server_get_list(JabberStream *js) { |
296 | 0 | JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS); |
297 | 0 | xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", |
298 | 0 | NS_DISCO_ITEMS); |
299 | |
|
300 | 0 | xmlnode_set_attrib(iq->node,"to",js->user->domain); |
301 | 0 | xmlnode_set_attrib(query,"node","http://jabber.org/protocol/commands"); |
302 | |
|
303 | 0 | jabber_iq_set_callback(iq,jabber_adhoc_server_got_list_cb,NULL); |
304 | 0 | jabber_iq_send(iq); |
305 | 0 | } |
306 | | |
307 | 0 | void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd) { |
308 | 0 | JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); |
309 | 0 | xmlnode *command = xmlnode_new_child(iq->node,"command"); |
310 | 0 | xmlnode_set_attrib(iq->node,"to",cmd->jid); |
311 | 0 | xmlnode_set_namespace(command,"http://jabber.org/protocol/commands"); |
312 | 0 | xmlnode_set_attrib(command,"node",cmd->node); |
313 | 0 | xmlnode_set_attrib(command,"action","execute"); |
314 | |
|
315 | 0 | jabber_iq_set_callback(iq,jabber_adhoc_parse,NULL); |
316 | |
|
317 | 0 | jabber_iq_send(iq); |
318 | 0 | } |
319 | | |
320 | 0 | static void jabber_adhoc_server_execute(PurplePluginAction *action) { |
321 | 0 | JabberAdHocCommands *cmd = action->user_data; |
322 | 0 | if(cmd) { |
323 | 0 | PurpleConnection *gc = (PurpleConnection *) action->context; |
324 | 0 | JabberStream *js = gc->proto_data; |
325 | |
|
326 | 0 | jabber_adhoc_execute(js, cmd); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | 0 | void jabber_adhoc_init_server_commands(JabberStream *js, GList **m) { |
331 | 0 | GList *cmdlst; |
332 | 0 | JabberBuddy *jb; |
333 | | |
334 | | /* also add commands for other clients connected to the same account on another resource */ |
335 | 0 | char *accountname = g_strdup_printf("%s@%s", js->user->node, js->user->domain); |
336 | 0 | if((jb = jabber_buddy_find(js, accountname, TRUE))) { |
337 | 0 | GList *iter; |
338 | 0 | for(iter = jb->resources; iter; iter = g_list_next(iter)) { |
339 | 0 | JabberBuddyResource *jbr = iter->data; |
340 | 0 | GList *riter; |
341 | 0 | for(riter = jbr->commands; riter; riter = g_list_next(riter)) { |
342 | 0 | JabberAdHocCommands *cmd = riter->data; |
343 | 0 | char *cmdname = g_strdup_printf("%s (%s)",cmd->name,jbr->name); |
344 | 0 | PurplePluginAction *act = purple_plugin_action_new(cmdname, jabber_adhoc_server_execute); |
345 | 0 | act->user_data = cmd; |
346 | 0 | *m = g_list_append(*m, act); |
347 | 0 | g_free(cmdname); |
348 | 0 | } |
349 | 0 | } |
350 | 0 | } |
351 | 0 | g_free(accountname); |
352 | | |
353 | | /* now add server commands */ |
354 | 0 | for(cmdlst = js->commands; cmdlst; cmdlst = g_list_next(cmdlst)) { |
355 | 0 | JabberAdHocCommands *cmd = cmdlst->data; |
356 | 0 | PurplePluginAction *act = purple_plugin_action_new(cmd->name, jabber_adhoc_server_execute); |
357 | 0 | act->user_data = cmd; |
358 | 0 | *m = g_list_append(*m, act); |
359 | 0 | } |
360 | 0 | } |