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(strcmp(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");
92 purple_xmlnode_insert_data(valuenode
, value
, -1);
95 case JABBER_X_DATA_TEXT_MULTI
:
98 const char *value
= purple_request_field_string_get_value(field
);
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);
112 case JABBER_X_DATA_LIST_SINGLE
:
113 case JABBER_X_DATA_LIST_MULTI
:
115 GList
*selected
= purple_request_field_list_get_selected(field
);
117 fieldnode
= purple_xmlnode_new_child(result
, "field");
118 purple_xmlnode_set_attrib(fieldnode
, "var", id
);
121 value
= purple_request_field_list_get_data(field
, selected
->data
);
122 valuenode
= purple_xmlnode_new_child(fieldnode
, "value");
124 purple_xmlnode_insert_data(valuenode
, value
, -1);
125 selected
= selected
->next
;
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);
136 purple_xmlnode_insert_data(valuenode
, "0", -1);
138 case JABBER_X_DATA_IGNORE
:
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
);
151 for(action
= data
->actions
; action
; action
= g_list_next(action
)) {
152 g_free(action
->data
);
154 g_list_free(data
->actions
);
159 cb(js
, result
, actionhandle
, user_data
);
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
);
180 for(action
= data
->actions
; action
; action
= g_list_next(action
)) {
181 g_free(action
->data
);
183 g_list_free(data
->actions
);
187 purple_xmlnode_set_namespace(result
, "jabber:x:data");
188 purple_xmlnode_set_attrib(result
, "type", "cancel");
191 cb(js
, result
, NULL
, user_data
);
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
)
204 PurpleXmlNode
*fn
, *x
;
205 PurpleRequestFields
*fields
;
206 PurpleRequestFieldGroup
*group
;
207 PurpleRequestField
*field
= 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
;
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");
231 type
= "text-single";
233 if(!var
&& strcmp(type
, "fixed"))
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
));
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
)))
259 g_string_append_printf(str
, "%s\n", value
);
263 field
= purple_request_field_string_new(var
, label
,
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
));
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
);
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
)) {
297 if(!(valuenode
= purple_xmlnode_get_child(optnode
, "value")))
300 if(!(value
= purple_xmlnode_get_data(valuenode
)))
303 if(!(lbl
= purple_xmlnode_get_attrib(optnode
, "label")))
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
);
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")))
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
));
335 } else if(!strcmp(type
, "fixed")) {
336 if((valuenode
= purple_xmlnode_get_child(fn
, "value")))
337 value
= purple_xmlnode_get_data(valuenode
);
340 field
= purple_request_field_label_new("", value
);
341 purple_request_field_group_add_field(group
, field
);
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
: "",
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
));
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
));
369 g_hash_table_replace(data
->fields
, g_strdup(var
), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE
));
375 if(field
&& purple_xmlnode_get_child(fn
, "required"))
376 purple_request_field_set_required(field
,TRUE
);
379 if(actions
!= NULL
) {
380 PurpleRequestField
*actionfield
;
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
),
412 g_free(instructions
);
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");
430 return purple_xmlnode_get_data(value
);
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.
440 /* Erm, none found :( */