Coverage Report

Created: 2026-04-01 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}