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 g_slist_free_full(data
->values
, g_free
);
145 g_list_free_full(data
->actions
, g_free
);
149 cb(js
, result
, actionhandle
, user_data
);
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
);
167 purple_xmlnode_set_namespace(result
, "jabber:x:data");
168 purple_xmlnode_set_attrib(result
, "type", "cancel");
171 cb(js
, result
, NULL
, user_data
);
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
)
184 PurpleXmlNode
*fn
, *x
;
185 PurpleRequestFields
*fields
;
186 PurpleRequestFieldGroup
*group
;
187 PurpleRequestField
*field
= 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
;
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");
211 type
= "text-single";
213 if(!var
&& !purple_strequal(type
, "fixed"))
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
));
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
)))
239 g_string_append_printf(str
, "%s\n", value
);
243 field
= purple_request_field_string_new(var
, label
,
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
));
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
);
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
)) {
277 if(!(valuenode
= purple_xmlnode_get_child(optnode
, "value")))
280 if(!(value
= purple_xmlnode_get_data(valuenode
)))
283 if(!(lbl
= purple_xmlnode_get_attrib(optnode
, "label")))
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")))
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
));
311 } else if(purple_strequal(type
, "fixed")) {
312 if((valuenode
= purple_xmlnode_get_child(fn
, "value")))
313 value
= purple_xmlnode_get_data(valuenode
);
316 field
= purple_request_field_label_new("", value
);
317 purple_request_field_group_add_field(group
, field
);
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
: "",
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
));
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
));
345 g_hash_table_replace(data
->fields
, g_strdup(var
), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE
));
351 if(field
&& purple_xmlnode_get_child(fn
, "required"))
352 purple_request_field_set_required(field
,TRUE
);
355 if(actions
!= NULL
) {
356 PurpleRequestField
*actionfield
;
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
),
388 g_free(instructions
);
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");
406 return purple_xmlnode_get_data(value
);
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.
416 /* Erm, none found :( */