Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / protocols / jabber / xdata.c
blob140eb879f5061fb82fb16aad2253bf131874ec3f
1 /*
2 * purple - Jabber Protocol Plugin
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.
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.
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.
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
23 #include "internal.h"
24 #include "request.h"
25 #include "server.h"
27 #include "xdata.h"
29 typedef enum {
30 JABBER_X_DATA_IGNORE = 0,
31 JABBER_X_DATA_TEXT_SINGLE,
32 JABBER_X_DATA_TEXT_MULTI,
33 JABBER_X_DATA_LIST_SINGLE,
34 JABBER_X_DATA_LIST_MULTI,
35 JABBER_X_DATA_BOOLEAN,
36 JABBER_X_DATA_JID_SINGLE
37 } jabber_x_data_field_type;
39 struct jabber_x_data_data {
40 GHashTable *fields;
41 GSList *values;
42 jabber_x_data_action_cb cb;
43 gpointer user_data;
44 JabberStream *js;
45 GList *actions;
46 PurpleRequestFieldGroup *actiongroup;
49 static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
50 PurpleXmlNode *result = purple_xmlnode_new("x");
51 jabber_x_data_action_cb cb = data->cb;
52 gpointer user_data = data->user_data;
53 JabberStream *js = data->js;
54 GList *groups, *flds;
55 char *actionhandle = NULL;
56 gboolean hasActions = (data->actions != NULL);
58 purple_xmlnode_set_namespace(result, "jabber:x:data");
59 purple_xmlnode_set_attrib(result, "type", "submit");
61 for(groups = purple_request_fields_get_groups(fields); groups; groups = groups->next) {
62 if(groups->data == data->actiongroup) {
63 for(flds = purple_request_field_group_get_fields(groups->data); flds; flds = flds->next) {
64 PurpleRequestField *field = flds->data;
65 const char *id = purple_request_field_get_id(field);
66 int handleindex;
67 if(strcmp(id, "libpurple:jabber:xdata:actions"))
68 continue;
69 handleindex = GPOINTER_TO_INT(purple_request_field_choice_get_value(field));
70 actionhandle = g_strdup(g_list_nth_data(data->actions, handleindex));
71 break;
73 continue;
75 for(flds = purple_request_field_group_get_fields(groups->data); flds; flds = flds->next) {
76 PurpleXmlNode *fieldnode, *valuenode;
77 PurpleRequestField *field = flds->data;
78 const char *id = purple_request_field_get_id(field);
79 jabber_x_data_field_type type = GPOINTER_TO_INT(g_hash_table_lookup(data->fields, id));
81 switch(type) {
82 case JABBER_X_DATA_TEXT_SINGLE:
83 case JABBER_X_DATA_JID_SINGLE:
85 const char *value = purple_request_field_string_get_value(field);
86 if (value == NULL)
87 break;
88 fieldnode = purple_xmlnode_new_child(result, "field");
89 purple_xmlnode_set_attrib(fieldnode, "var", id);
90 valuenode = purple_xmlnode_new_child(fieldnode, "value");
91 if(value)
92 purple_xmlnode_insert_data(valuenode, value, -1);
93 break;
95 case JABBER_X_DATA_TEXT_MULTI:
97 char **pieces, **p;
98 const char *value = purple_request_field_string_get_value(field);
99 if (value == NULL)
100 break;
101 fieldnode = purple_xmlnode_new_child(result, "field");
102 purple_xmlnode_set_attrib(fieldnode, "var", id);
104 pieces = g_strsplit(value, "\n", -1);
105 for(p = pieces; *p != NULL; p++) {
106 valuenode = purple_xmlnode_new_child(fieldnode, "value");
107 purple_xmlnode_insert_data(valuenode, *p, -1);
109 g_strfreev(pieces);
111 break;
112 case JABBER_X_DATA_LIST_SINGLE:
113 case JABBER_X_DATA_LIST_MULTI:
115 GList *selected = purple_request_field_list_get_selected(field);
116 char *value;
117 fieldnode = purple_xmlnode_new_child(result, "field");
118 purple_xmlnode_set_attrib(fieldnode, "var", id);
120 while(selected) {
121 value = purple_request_field_list_get_data(field, selected->data);
122 valuenode = purple_xmlnode_new_child(fieldnode, "value");
123 if(value)
124 purple_xmlnode_insert_data(valuenode, value, -1);
125 selected = selected->next;
128 break;
129 case JABBER_X_DATA_BOOLEAN:
130 fieldnode = purple_xmlnode_new_child(result, "field");
131 purple_xmlnode_set_attrib(fieldnode, "var", id);
132 valuenode = purple_xmlnode_new_child(fieldnode, "value");
133 if(purple_request_field_bool_get_value(field))
134 purple_xmlnode_insert_data(valuenode, "1", -1);
135 else
136 purple_xmlnode_insert_data(valuenode, "0", -1);
137 break;
138 case JABBER_X_DATA_IGNORE:
139 break;
144 g_hash_table_destroy(data->fields);
145 while(data->values) {
146 g_free(data->values->data);
147 data->values = g_slist_delete_link(data->values, data->values);
149 if (data->actions) {
150 GList *action;
151 for(action = data->actions; action; action = g_list_next(action)) {
152 g_free(action->data);
154 g_list_free(data->actions);
156 g_free(data);
158 if (hasActions)
159 cb(js, result, actionhandle, user_data);
160 else
161 ((jabber_x_data_cb)cb)(js, result, user_data);
163 g_free(actionhandle);
166 static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
167 PurpleXmlNode *result = purple_xmlnode_new("x");
168 jabber_x_data_action_cb cb = data->cb;
169 gpointer user_data = data->user_data;
170 JabberStream *js = data->js;
171 gboolean hasActions = FALSE;
172 g_hash_table_destroy(data->fields);
173 while(data->values) {
174 g_free(data->values->data);
175 data->values = g_slist_delete_link(data->values, data->values);
177 if (data->actions) {
178 GList *action;
179 hasActions = TRUE;
180 for(action = data->actions; action; action = g_list_next(action)) {
181 g_free(action->data);
183 g_list_free(data->actions);
185 g_free(data);
187 purple_xmlnode_set_namespace(result, "jabber:x:data");
188 purple_xmlnode_set_attrib(result, "type", "cancel");
190 if (hasActions)
191 cb(js, result, NULL, user_data);
192 else
193 ((jabber_x_data_cb)cb)(js, result, user_data);
196 void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data)
198 return jabber_x_data_request_with_actions(js, packet, NULL, 0, (jabber_x_data_action_cb)cb, user_data);
201 void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
203 void *handle;
204 PurpleXmlNode *fn, *x;
205 PurpleRequestFields *fields;
206 PurpleRequestFieldGroup *group;
207 PurpleRequestField *field = NULL;
209 char *title = NULL;
210 char *instructions = NULL;
212 struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1);
214 data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
215 data->user_data = user_data;
216 data->cb = cb;
217 data->js = js;
219 fields = purple_request_fields_new();
220 group = purple_request_field_group_new(NULL);
221 purple_request_fields_add_group(fields, group);
223 for(fn = purple_xmlnode_get_child(packet, "field"); fn; fn = purple_xmlnode_get_next_twin(fn)) {
224 PurpleXmlNode *valuenode;
225 const char *type = purple_xmlnode_get_attrib(fn, "type");
226 const char *label = purple_xmlnode_get_attrib(fn, "label");
227 const char *var = purple_xmlnode_get_attrib(fn, "var");
228 char *value = NULL;
230 if(!type)
231 type = "text-single";
233 if(!var && strcmp(type, "fixed"))
234 continue;
235 if(!label)
236 label = var;
238 if(!strcmp(type, "text-private")) {
239 if((valuenode = purple_xmlnode_get_child(fn, "value")))
240 value = purple_xmlnode_get_data(valuenode);
242 field = purple_request_field_string_new(var, label,
243 value ? value : "", FALSE);
244 purple_request_field_string_set_masked(field, TRUE);
245 purple_request_field_group_add_field(group, field);
247 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
249 g_free(value);
250 } else if(!strcmp(type, "text-multi") || !strcmp(type, "jid-multi")) {
251 GString *str = g_string_new("");
253 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
254 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
256 if(!(value = purple_xmlnode_get_data(valuenode)))
257 continue;
259 g_string_append_printf(str, "%s\n", value);
260 g_free(value);
263 field = purple_request_field_string_new(var, label,
264 str->str, TRUE);
265 purple_request_field_group_add_field(group, field);
267 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_MULTI));
269 g_string_free(str, TRUE);
270 } else if(!strcmp(type, "list-single") || !strcmp(type, "list-multi")) {
271 PurpleXmlNode *optnode;
272 GList *selected = NULL;
274 field = purple_request_field_list_new(var, label);
276 if(!strcmp(type, "list-multi")) {
277 purple_request_field_list_set_multi_select(field, TRUE);
278 g_hash_table_replace(data->fields, g_strdup(var),
279 GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI));
280 } else {
281 g_hash_table_replace(data->fields, g_strdup(var),
282 GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE));
285 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
286 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
287 char *data = purple_xmlnode_get_data(valuenode);
288 if (data != NULL) {
289 selected = g_list_prepend(selected, data);
293 for(optnode = purple_xmlnode_get_child(fn, "option"); optnode;
294 optnode = purple_xmlnode_get_next_twin(optnode)) {
295 const char *lbl;
297 if(!(valuenode = purple_xmlnode_get_child(optnode, "value")))
298 continue;
300 if(!(value = purple_xmlnode_get_data(valuenode)))
301 continue;
303 if(!(lbl = purple_xmlnode_get_attrib(optnode, "label")))
304 lbl = value;
306 data->values = g_slist_prepend(data->values, value);
308 purple_request_field_list_add_icon(field, lbl, NULL, value);
309 if(g_list_find_custom(selected, value, (GCompareFunc)strcmp))
310 purple_request_field_list_add_selected(field, lbl);
312 purple_request_field_group_add_field(group, field);
314 while(selected) {
315 g_free(selected->data);
316 selected = g_list_delete_link(selected, selected);
319 } else if(!strcmp(type, "boolean")) {
320 gboolean def = FALSE;
322 if((valuenode = purple_xmlnode_get_child(fn, "value")))
323 value = purple_xmlnode_get_data(valuenode);
325 if(value && (!g_ascii_strcasecmp(value, "yes") ||
326 !g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1")))
327 def = TRUE;
329 field = purple_request_field_bool_new(var, label, def);
330 purple_request_field_group_add_field(group, field);
332 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN));
334 g_free(value);
335 } else if(!strcmp(type, "fixed")) {
336 if((valuenode = purple_xmlnode_get_child(fn, "value")))
337 value = purple_xmlnode_get_data(valuenode);
339 if(value != NULL) {
340 field = purple_request_field_label_new("", value);
341 purple_request_field_group_add_field(group, field);
343 g_free(value);
345 } else if(!strcmp(type, "hidden")) {
346 if((valuenode = purple_xmlnode_get_child(fn, "value")))
347 value = purple_xmlnode_get_data(valuenode);
349 field = purple_request_field_string_new(var, "", value ? value : "",
350 FALSE);
351 purple_request_field_set_visible(field, FALSE);
352 purple_request_field_group_add_field(group, field);
354 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
356 g_free(value);
357 } else { /* text-single, jid-single, and the default */
358 if((valuenode = purple_xmlnode_get_child(fn, "value")))
359 value = purple_xmlnode_get_data(valuenode);
361 field = purple_request_field_string_new(var, label,
362 value ? value : "", FALSE);
363 purple_request_field_group_add_field(group, field);
365 if(!strcmp(type, "jid-single")) {
366 purple_request_field_set_type_hint(field, "screenname");
367 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_JID_SINGLE));
368 } else {
369 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
372 g_free(value);
375 if(field && purple_xmlnode_get_child(fn, "required"))
376 purple_request_field_set_required(field,TRUE);
379 if(actions != NULL) {
380 PurpleRequestField *actionfield;
381 GList *action;
382 int i;
384 data->actiongroup = group = purple_request_field_group_new(_("Actions"));
385 purple_request_fields_add_group(fields, group);
386 actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), GINT_TO_POINTER(defaultaction));
387 purple_request_field_choice_set_data_destructor(actionfield, g_free);
389 for(i = 0, action = actions; action; action = g_list_next(action), i++) {
390 JabberXDataAction *a = action->data;
392 purple_request_field_choice_add(actionfield, a->name, GINT_TO_POINTER(i));
393 data->actions = g_list_append(data->actions, g_strdup(a->handle));
395 purple_request_field_set_required(actionfield,TRUE);
396 purple_request_field_group_add_field(group, actionfield);
399 if((x = purple_xmlnode_get_child(packet, "title")))
400 title = purple_xmlnode_get_data(x);
402 if((x = purple_xmlnode_get_child(packet, "instructions")))
403 instructions = purple_xmlnode_get_data(x);
405 handle = purple_request_fields(js->gc, title, title, instructions, fields,
406 _("OK"), G_CALLBACK(jabber_x_data_ok_cb),
407 _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb),
408 purple_request_cpar_from_connection(js->gc),
409 data);
411 g_free(title);
412 g_free(instructions);
414 return handle;
417 gchar *
418 jabber_x_data_get_formtype(const PurpleXmlNode *form)
420 PurpleXmlNode *field;
422 g_return_val_if_fail(form != NULL, NULL);
424 for (field = purple_xmlnode_get_child((PurpleXmlNode *)form, "field"); field;
425 field = purple_xmlnode_get_next_twin(field)) {
426 const char *var = purple_xmlnode_get_attrib(field, "var");
427 if (purple_strequal(var, "FORM_TYPE")) {
428 PurpleXmlNode *value = purple_xmlnode_get_child(field, "value");
429 if (value)
430 return purple_xmlnode_get_data(value);
431 else
432 /* An interesting corner case... Looking for a second
433 * FORM_TYPE would be more considerate, but I'm in favor
434 * of not helping broken clients.
436 return NULL;
440 /* Erm, none found :( */
441 return NULL;