Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / protocols / jabber / xdata.c
blob962b67c3195704ea17b60c1e3b8e0551b47a0d30
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 g_slist_free_full(data->values, g_free);
145 g_list_free_full(data->actions, g_free);
146 g_free(data);
148 if (hasActions)
149 cb(js, result, actionhandle, user_data);
150 else
151 ((jabber_x_data_cb)cb)(js, result, user_data);
153 g_free(actionhandle);
156 static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, PurpleRequestFields *fields) {
157 PurpleXmlNode *result = purple_xmlnode_new("x");
158 jabber_x_data_action_cb cb = data->cb;
159 gpointer user_data = data->user_data;
160 JabberStream *js = data->js;
161 gboolean hasActions = (data->actions != NULL);
162 g_hash_table_destroy(data->fields);
163 g_slist_free_full(data->values, g_free);
164 g_list_free_full(data->actions, g_free);
165 g_free(data);
167 purple_xmlnode_set_namespace(result, "jabber:x:data");
168 purple_xmlnode_set_attrib(result, "type", "cancel");
170 if (hasActions)
171 cb(js, result, NULL, user_data);
172 else
173 ((jabber_x_data_cb)cb)(js, result, user_data);
176 void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data)
178 return jabber_x_data_request_with_actions(js, packet, NULL, 0, (jabber_x_data_action_cb)cb, user_data);
181 void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data)
183 void *handle;
184 PurpleXmlNode *fn, *x;
185 PurpleRequestFields *fields;
186 PurpleRequestFieldGroup *group;
187 PurpleRequestField *field = NULL;
189 char *title = NULL;
190 char *instructions = NULL;
192 struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1);
194 data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
195 data->user_data = user_data;
196 data->cb = cb;
197 data->js = js;
199 fields = purple_request_fields_new();
200 group = purple_request_field_group_new(NULL);
201 purple_request_fields_add_group(fields, group);
203 for(fn = purple_xmlnode_get_child(packet, "field"); fn; fn = purple_xmlnode_get_next_twin(fn)) {
204 PurpleXmlNode *valuenode;
205 const char *type = purple_xmlnode_get_attrib(fn, "type");
206 const char *label = purple_xmlnode_get_attrib(fn, "label");
207 const char *var = purple_xmlnode_get_attrib(fn, "var");
208 char *value = NULL;
210 if(!type)
211 type = "text-single";
213 if(!var && !purple_strequal(type, "fixed"))
214 continue;
215 if(!label)
216 label = var;
218 if(purple_strequal(type, "text-private")) {
219 if((valuenode = purple_xmlnode_get_child(fn, "value")))
220 value = purple_xmlnode_get_data(valuenode);
222 field = purple_request_field_string_new(var, label,
223 value ? value : "", FALSE);
224 purple_request_field_string_set_masked(field, TRUE);
225 purple_request_field_group_add_field(group, field);
227 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
229 g_free(value);
230 } else if(purple_strequal(type, "text-multi") || purple_strequal(type, "jid-multi")) {
231 GString *str = g_string_new("");
233 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
234 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
236 if(!(value = purple_xmlnode_get_data(valuenode)))
237 continue;
239 g_string_append_printf(str, "%s\n", value);
240 g_free(value);
243 field = purple_request_field_string_new(var, label,
244 str->str, 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_MULTI));
249 g_string_free(str, TRUE);
250 } else if(purple_strequal(type, "list-single") || purple_strequal(type, "list-multi")) {
251 PurpleXmlNode *optnode;
252 GList *selected = NULL;
254 field = purple_request_field_list_new(var, label);
256 if(purple_strequal(type, "list-multi")) {
257 purple_request_field_list_set_multi_select(field, TRUE);
258 g_hash_table_replace(data->fields, g_strdup(var),
259 GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI));
260 } else {
261 g_hash_table_replace(data->fields, g_strdup(var),
262 GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE));
265 for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode;
266 valuenode = purple_xmlnode_get_next_twin(valuenode)) {
267 char *data = purple_xmlnode_get_data(valuenode);
268 if (data != NULL) {
269 selected = g_list_prepend(selected, data);
273 for(optnode = purple_xmlnode_get_child(fn, "option"); optnode;
274 optnode = purple_xmlnode_get_next_twin(optnode)) {
275 const char *lbl;
277 if(!(valuenode = purple_xmlnode_get_child(optnode, "value")))
278 continue;
280 if(!(value = purple_xmlnode_get_data(valuenode)))
281 continue;
283 if(!(lbl = purple_xmlnode_get_attrib(optnode, "label")))
284 lbl = value;
286 data->values = g_slist_prepend(data->values, value);
288 purple_request_field_list_add_icon(field, lbl, NULL, value);
289 if(g_list_find_custom(selected, value, (GCompareFunc)strcmp))
290 purple_request_field_list_add_selected(field, lbl);
292 purple_request_field_group_add_field(group, field);
294 g_list_free_full(selected, g_free);
295 } else if(purple_strequal(type, "boolean")) {
296 gboolean def = FALSE;
298 if((valuenode = purple_xmlnode_get_child(fn, "value")))
299 value = purple_xmlnode_get_data(valuenode);
301 if(value && (!g_ascii_strcasecmp(value, "yes") ||
302 !g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1")))
303 def = TRUE;
305 field = purple_request_field_bool_new(var, label, def);
306 purple_request_field_group_add_field(group, field);
308 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN));
310 g_free(value);
311 } else if(purple_strequal(type, "fixed")) {
312 if((valuenode = purple_xmlnode_get_child(fn, "value")))
313 value = purple_xmlnode_get_data(valuenode);
315 if(value != NULL) {
316 field = purple_request_field_label_new("", value);
317 purple_request_field_group_add_field(group, field);
319 g_free(value);
321 } else if(purple_strequal(type, "hidden")) {
322 if((valuenode = purple_xmlnode_get_child(fn, "value")))
323 value = purple_xmlnode_get_data(valuenode);
325 field = purple_request_field_string_new(var, "", value ? value : "",
326 FALSE);
327 purple_request_field_set_visible(field, FALSE);
328 purple_request_field_group_add_field(group, field);
330 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
332 g_free(value);
333 } else { /* text-single, jid-single, and the default */
334 if((valuenode = purple_xmlnode_get_child(fn, "value")))
335 value = purple_xmlnode_get_data(valuenode);
337 field = purple_request_field_string_new(var, label,
338 value ? value : "", FALSE);
339 purple_request_field_group_add_field(group, field);
341 if(purple_strequal(type, "jid-single")) {
342 purple_request_field_set_type_hint(field, "screenname");
343 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_JID_SINGLE));
344 } else {
345 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE));
348 g_free(value);
351 if(field && purple_xmlnode_get_child(fn, "required"))
352 purple_request_field_set_required(field,TRUE);
355 if(actions != NULL) {
356 PurpleRequestField *actionfield;
357 GList *action;
358 int i;
360 data->actiongroup = group = purple_request_field_group_new(_("Actions"));
361 purple_request_fields_add_group(fields, group);
362 actionfield = purple_request_field_choice_new("libpurple:jabber:xdata:actions", _("Select an action"), GINT_TO_POINTER(defaultaction));
363 purple_request_field_choice_set_data_destructor(actionfield, g_free);
365 for(i = 0, action = actions; action; action = g_list_next(action), i++) {
366 JabberXDataAction *a = action->data;
368 purple_request_field_choice_add(actionfield, a->name, GINT_TO_POINTER(i));
369 data->actions = g_list_append(data->actions, g_strdup(a->handle));
371 purple_request_field_set_required(actionfield,TRUE);
372 purple_request_field_group_add_field(group, actionfield);
375 if((x = purple_xmlnode_get_child(packet, "title")))
376 title = purple_xmlnode_get_data(x);
378 if((x = purple_xmlnode_get_child(packet, "instructions")))
379 instructions = purple_xmlnode_get_data(x);
381 handle = purple_request_fields(js->gc, title, title, instructions, fields,
382 _("OK"), G_CALLBACK(jabber_x_data_ok_cb),
383 _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb),
384 purple_request_cpar_from_connection(js->gc),
385 data);
387 g_free(title);
388 g_free(instructions);
390 return handle;
393 gchar *
394 jabber_x_data_get_formtype(const PurpleXmlNode *form)
396 PurpleXmlNode *field;
398 g_return_val_if_fail(form != NULL, NULL);
400 for (field = purple_xmlnode_get_child((PurpleXmlNode *)form, "field"); field;
401 field = purple_xmlnode_get_next_twin(field)) {
402 const char *var = purple_xmlnode_get_attrib(field, "var");
403 if (purple_strequal(var, "FORM_TYPE")) {
404 PurpleXmlNode *value = purple_xmlnode_get_child(field, "value");
405 if (value)
406 return purple_xmlnode_get_data(value);
407 else
408 /* An interesting corner case... Looking for a second
409 * FORM_TYPE would be more considerate, but I'm in favor
410 * of not helping broken clients.
412 return NULL;
416 /* Erm, none found :( */
417 return NULL;