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
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
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
{
42 jabber_x_data_action_cb cb
;
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
;
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
);
67 if(!purple_strequal(id
, "libpurple:jabber:xdata:actions"))
69 handleindex
= GPOINTER_TO_INT(purple_request_field_choice_get_value(field
));
70 actionhandle
= g_strdup(g_list_nth_data(data
->actions
, handleindex
));
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
));
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
);
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);
94 case JABBER_X_DATA_TEXT_MULTI
:
97 const char *value
= purple_request_field_string_get_value(field
);
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);
111 case JABBER_X_DATA_LIST_SINGLE
:
112 case JABBER_X_DATA_LIST_MULTI
:
114 GList
*selected
= purple_request_field_list_get_selected(field
);
116 fieldnode
= purple_xmlnode_new_child(result
, "field");
117 purple_xmlnode_set_attrib(fieldnode
, "var", id
);
120 value
= purple_request_field_list_get_data(field
, selected
->data
);
121 valuenode
= purple_xmlnode_new_child(fieldnode
, "value");
123 purple_xmlnode_insert_data(valuenode
, value
, -1);
124 selected
= selected
->next
;
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);
135 purple_xmlnode_insert_data(valuenode
, "0", -1);
137 case JABBER_X_DATA_IGNORE
:
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
);
150 for(action
= data
->actions
; action
; action
= g_list_next(action
)) {
151 g_free(action
->data
);
153 g_list_free(data
->actions
);
158 cb(js
, result
, actionhandle
, user_data
);
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
);
179 for(action
= data
->actions
; action
; action
= g_list_next(action
)) {
180 g_free(action
->data
);
182 g_list_free(data
->actions
);
186 purple_xmlnode_set_namespace(result
, "jabber:x:data");
187 purple_xmlnode_set_attrib(result
, "type", "cancel");
190 cb(js
, result
, NULL
, user_data
);
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
)
203 PurpleXmlNode
*fn
, *x
;
204 PurpleRequestFields
*fields
;
205 PurpleRequestFieldGroup
*group
;
206 PurpleRequestField
*field
= 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
;
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");
230 type
= "text-single";
232 if(!var
&& !purple_strequal(type
, "fixed"))
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
));
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
)))
258 g_string_append_printf(str
, "%s\n", value
);
262 field
= purple_request_field_string_new(var
, label
,
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
));
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
);
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
)) {
296 if(!(valuenode
= purple_xmlnode_get_child(optnode
, "value")))
299 if(!(value
= purple_xmlnode_get_data(valuenode
)))
302 if(!(lbl
= purple_xmlnode_get_attrib(optnode
, "label")))
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
);
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")))
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
));
334 } else if(purple_strequal(type
, "fixed")) {
335 if((valuenode
= purple_xmlnode_get_child(fn
, "value")))
336 value
= purple_xmlnode_get_data(valuenode
);
339 field
= purple_request_field_label_new("", value
);
340 purple_request_field_group_add_field(group
, field
);
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
: "",
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
));
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
));
368 g_hash_table_replace(data
->fields
, g_strdup(var
), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE
));
374 if(field
&& purple_xmlnode_get_child(fn
, "required"))
375 purple_request_field_set_required(field
,TRUE
);
378 if(actions
!= NULL
) {
379 PurpleRequestField
*actionfield
;
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
),
411 g_free(instructions
);
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");
429 return purple_xmlnode_get_data(value
);
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.
439 /* Erm, none found :( */