rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / jabber / xdata.c
blob81b5448281612a6215e8cbfa281318aff7bf2b68
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(!purple_strequal(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 purple_xmlnode_insert_data(valuenode, value, -1);
92 break;
94 case JABBER_X_DATA_TEXT_MULTI:
96 char **pieces, **p;
97 const char *value = purple_request_field_string_get_value(field);
98 if (value == NULL)
99 break;
100 fieldnode = purple_xmlnode_new_child(result, "field");
101 purple_xmlnode_set_attrib(fieldnode, "var", id);
103 pieces = g_strsplit(value, "\n", -1);
104 for(p = pieces; *p != NULL; p++) {
105 valuenode = purple_xmlnode_new_child(fieldnode, "value");
106 purple_xmlnode_insert_data(valuenode, *p, -1);
108 g_strfreev(pieces);
110 break;
111 case JABBER_X_DATA_LIST_SINGLE:
112 case JABBER_X_DATA_LIST_MULTI:
114 GList *selected = purple_request_field_list_get_selected(field);
115 char *value;
116 fieldnode = purple_xmlnode_new_child(result, "field");
117 purple_xmlnode_set_attrib(fieldnode, "var", id);
119 while(selected) {
120 value = purple_request_field_list_get_data(field, selected->data);
121 valuenode = purple_xmlnode_new_child(fieldnode, "value");
122 if(value)
123 purple_xmlnode_insert_data(valuenode, value, -1);
124 selected = selected->next;
127 break;
128 case JABBER_X_DATA_BOOLEAN:
129 fieldnode = purple_xmlnode_new_child(result, "field");
130 purple_xmlnode_set_attrib(fieldnode, "var", id);
131 valuenode = purple_xmlnode_new_child(fieldnode, "value");
132 if(purple_request_field_bool_get_value(field))
133 purple_xmlnode_insert_data(valuenode, "1", -1);
134 else
135 purple_xmlnode_insert_data(valuenode, "0", -1);
136 break;
137 case JABBER_X_DATA_IGNORE:
138 break;
143 g_hash_table_destroy(data->fields);
144 while(data->values) {
145 g_free(data->values->data);
146 data->values = g_slist_delete_link(data->values, data->values);
148 if (data->actions) {
149 GList *action;
150 for(action = data->actions; action; action = g_list_next(action)) {
151 g_free(action->data);
153 g_list_free(data->actions);
155 g_free(data);
157 if (hasActions)
158 cb(js, result, actionhandle, user_data);
159 else
160 ((jabber_x_data_cb)cb)(js, result, user_data);
162 g_free(actionhandle);
165 static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
166 PurpleXmlNode *result = purple_xmlnode_new("x");
167 jabber_x_data_action_cb cb = data->cb;
168 gpointer user_data = data->user_data;
169 JabberStream *js = data->js;
170 gboolean hasActions = FALSE;
171 g_hash_table_destroy(data->fields);
172 while(data->values) {
173 g_free(data->values->data);
174 data->values = g_slist_delete_link(data->values, data->values);
176 if (data->actions) {
177 GList *action;
178 hasActions = TRUE;
179 for(action = data->actions; action; action = g_list_next(action)) {
180 g_free(action->data);
182 g_list_free(data->actions);
184 g_free(data);
186 purple_xmlnode_set_namespace(result, "jabber:x:data");
187 purple_xmlnode_set_attrib(result, "type", "cancel");
189 if (hasActions)
190 cb(js, result, NULL, user_data);
191 else
192 ((jabber_x_data_cb)cb)(js, result, user_data);
195 void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data)
197 return jabber_x_data_request_with_actions(js, packet, NULL, 0, (jabber_x_data_action_cb)cb, user_data);
200 void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
202 void *handle;
203 PurpleXmlNode *fn, *x;
204 PurpleRequestFields *fields;
205 PurpleRequestFieldGroup *group;
206 PurpleRequestField *field = NULL;
208 char *title = NULL;
209 char *instructions = NULL;
211 struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1);
213 data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
214 data->user_data = user_data;
215 data->cb = cb;
216 data->js = js;
218 fields = purple_request_fields_new();
219 group = purple_request_field_group_new(NULL);
220 purple_request_fields_add_group(fields, group);
222 for(fn = purple_xmlnode_get_child(packet, "field"); fn; fn = purple_xmlnode_get_next_twin(fn)) {
223 PurpleXmlNode *valuenode;
224 const char *type = purple_xmlnode_get_attrib(fn, "type");
225 const char *label = purple_xmlnode_get_attrib(fn, "label");
226 const char *var = purple_xmlnode_get_attrib(fn, "var");
227 char *value = NULL;
229 if(!type)
230 type = "text-single";
232 if(!var && !purple_strequal(type, "fixed"))
233 continue;
234 if(!label)
235 label = var;
237 if(purple_strequal(type, "text-private")) {
238 if((valuenode = purple_xmlnode_get_child(fn, "value")))
239 value = purple_xmlnode_get_data(valuenode);
241 field = purple_request_field_string_new(var, label,
242 value ? value : "", FALSE);
243 purple_request_field_string_set_masked(field, TRUE);
244 purple_request_field_group_add_field(group, field);
246 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
248 g_free(value);
249 } else if(purple_strequal(type, "text-multi") || purple_strequal(type, "jid-multi")) {
250 GString *str = g_string_new("");
252 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
253 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
255 if(!(value = purple_xmlnode_get_data(valuenode)))
256 continue;
258 g_string_append_printf(str, "%s\n", value);
259 g_free(value);
262 field = purple_request_field_string_new(var, label,
263 str->str, TRUE);
264 purple_request_field_group_add_field(group, field);
266 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_MULTI));
268 g_string_free(str, TRUE);
269 } else if(purple_strequal(type, "list-single") || purple_strequal(type, "list-multi")) {
270 PurpleXmlNode *optnode;
271 GList *selected = NULL;
273 field = purple_request_field_list_new(var, label);
275 if(purple_strequal(type, "list-multi")) {
276 purple_request_field_list_set_multi_select(field, TRUE);
277 g_hash_table_replace(data->fields, g_strdup(var),
278 GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI));
279 } else {
280 g_hash_table_replace(data->fields, g_strdup(var),
281 GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE));
284 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
285 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
286 char *data = purple_xmlnode_get_data(valuenode);
287 if (data != NULL) {
288 selected = g_list_prepend(selected, data);
292 for(optnode = purple_xmlnode_get_child(fn, "option"); optnode;
293 optnode = purple_xmlnode_get_next_twin(optnode)) {
294 const char *lbl;
296 if(!(valuenode = purple_xmlnode_get_child(optnode, "value")))
297 continue;
299 if(!(value = purple_xmlnode_get_data(valuenode)))
300 continue;
302 if(!(lbl = purple_xmlnode_get_attrib(optnode, "label")))
303 lbl = value;
305 data->values = g_slist_prepend(data->values, value);
307 purple_request_field_list_add_icon(field, lbl, NULL, value);
308 if(g_list_find_custom(selected, value, (GCompareFunc)strcmp))
309 purple_request_field_list_add_selected(field, lbl);
311 purple_request_field_group_add_field(group, field);
313 while(selected) {
314 g_free(selected->data);
315 selected = g_list_delete_link(selected, selected);
318 } else if(purple_strequal(type, "boolean")) {
319 gboolean def = FALSE;
321 if((valuenode = purple_xmlnode_get_child(fn, "value")))
322 value = purple_xmlnode_get_data(valuenode);
324 if(value && (!g_ascii_strcasecmp(value, "yes") ||
325 !g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1")))
326 def = TRUE;
328 field = purple_request_field_bool_new(var, label, def);
329 purple_request_field_group_add_field(group, field);
331 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN));
333 g_free(value);
334 } else if(purple_strequal(type, "fixed")) {
335 if((valuenode = purple_xmlnode_get_child(fn, "value")))
336 value = purple_xmlnode_get_data(valuenode);
338 if(value != NULL) {
339 field = purple_request_field_label_new("", value);
340 purple_request_field_group_add_field(group, field);
342 g_free(value);
344 } else if(purple_strequal(type, "hidden")) {
345 if((valuenode = purple_xmlnode_get_child(fn, "value")))
346 value = purple_xmlnode_get_data(valuenode);
348 field = purple_request_field_string_new(var, "", value ? value : "",
349 FALSE);
350 purple_request_field_set_visible(field, FALSE);
351 purple_request_field_group_add_field(group, field);
353 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
355 g_free(value);
356 } else { /* text-single, jid-single, and the default */
357 if((valuenode = purple_xmlnode_get_child(fn, "value")))
358 value = purple_xmlnode_get_data(valuenode);
360 field = purple_request_field_string_new(var, label,
361 value ? value : "", FALSE);
362 purple_request_field_group_add_field(group, field);
364 if(purple_strequal(type, "jid-single")) {
365 purple_request_field_set_type_hint(field, "screenname");
366 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_JID_SINGLE));
367 } else {
368 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
371 g_free(value);
374 if(field && purple_xmlnode_get_child(fn, "required"))
375 purple_request_field_set_required(field,TRUE);
378 if(actions != NULL) {
379 PurpleRequestField *actionfield;
380 GList *action;
381 int i;
383 data->actiongroup = group = purple_request_field_group_new(_("Actions"));
384 purple_request_fields_add_group(fields, group);
385 actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), GINT_TO_POINTER(defaultaction));
386 purple_request_field_choice_set_data_destructor(actionfield, g_free);
388 for(i = 0, action = actions; action; action = g_list_next(action), i++) {
389 JabberXDataAction *a = action->data;
391 purple_request_field_choice_add(actionfield, a->name, GINT_TO_POINTER(i));
392 data->actions = g_list_append(data->actions, g_strdup(a->handle));
394 purple_request_field_set_required(actionfield,TRUE);
395 purple_request_field_group_add_field(group, actionfield);
398 if((x = purple_xmlnode_get_child(packet, "title")))
399 title = purple_xmlnode_get_data(x);
401 if((x = purple_xmlnode_get_child(packet, "instructions")))
402 instructions = purple_xmlnode_get_data(x);
404 handle = purple_request_fields(js->gc, title, title, instructions, fields,
405 _("OK"), G_CALLBACK(jabber_x_data_ok_cb),
406 _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb),
407 purple_request_cpar_from_connection(js->gc),
408 data);
410 g_free(title);
411 g_free(instructions);
413 return handle;
416 gchar *
417 jabber_x_data_get_formtype(const PurpleXmlNode *form)
419 PurpleXmlNode *field;
421 g_return_val_if_fail(form != NULL, NULL);
423 for (field = purple_xmlnode_get_child((PurpleXmlNode *)form, "field"); field;
424 field = purple_xmlnode_get_next_twin(field)) {
425 const char *var = purple_xmlnode_get_attrib(field, "var");
426 if (purple_strequal(var, "FORM_TYPE")) {
427 PurpleXmlNode *value = purple_xmlnode_get_child(field, "value");
428 if (value)
429 return purple_xmlnode_get_data(value);
430 else
431 /* An interesting corner case... Looking for a second
432 * FORM_TYPE would be more considerate, but I'm in favor
433 * of not helping broken clients.
435 return NULL;
439 /* Erm, none found :( */
440 return NULL;