| |
1 /* |
| |
2 * gaim - Jabber Protocol Plugin |
| |
3 * |
| |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> |
| |
5 * |
| |
6 * This program is free software; you can redistribute it and/or modify |
| |
7 * it under the terms of the GNU General Public License as published by |
| |
8 * the Free Software Foundation; either version 2 of the License, or |
| |
9 * (at your option) any later version. |
| |
10 * |
| |
11 * This program is distributed in the hope that it will be useful, |
| |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
14 * GNU General Public License for more details. |
| |
15 * |
| |
16 * You should have received a copy of the GNU General Public License |
| |
17 * along with this program; if not, write to the Free Software |
| |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
19 * |
| |
20 */ |
| |
21 #include "internal.h" |
| |
22 #include "request.h" |
| |
23 #include "server.h" |
| |
24 |
| |
25 #include "xdata.h" |
| |
26 |
| |
27 typedef enum { |
| |
28 JABBER_X_DATA_IGNORE = 0, |
| |
29 JABBER_X_DATA_TEXT_SINGLE, |
| |
30 JABBER_X_DATA_TEXT_MULTI, |
| |
31 JABBER_X_DATA_LIST_SINGLE, |
| |
32 JABBER_X_DATA_LIST_MULTI, |
| |
33 JABBER_X_DATA_BOOLEAN, |
| |
34 JABBER_X_DATA_JID_SINGLE |
| |
35 } jabber_x_data_field_type; |
| |
36 |
| |
37 struct jabber_x_data_data { |
| |
38 GHashTable *fields; |
| |
39 GSList *values; |
| |
40 jabber_x_data_cb cb; |
| |
41 gpointer user_data; |
| |
42 JabberStream *js; |
| |
43 }; |
| |
44 |
| |
45 static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, GaimRequestFields *fields) { |
| |
46 xmlnode *result = xmlnode_new("x"); |
| |
47 jabber_x_data_cb cb = data->cb; |
| |
48 gpointer user_data = data->user_data; |
| |
49 JabberStream *js = data->js; |
| |
50 GList *groups, *flds; |
| |
51 |
| |
52 xmlnode_set_namespace(result, "jabber:x:data"); |
| |
53 xmlnode_set_attrib(result, "type", "submit"); |
| |
54 |
| |
55 for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) { |
| |
56 for(flds = gaim_request_field_group_get_fields(groups->data); flds; flds = flds->next) { |
| |
57 xmlnode *fieldnode, *valuenode; |
| |
58 GaimRequestField *field = flds->data; |
| |
59 const char *id = gaim_request_field_get_id(field); |
| |
60 jabber_x_data_field_type type = GPOINTER_TO_INT(g_hash_table_lookup(data->fields, id)); |
| |
61 |
| |
62 switch(type) { |
| |
63 case JABBER_X_DATA_TEXT_SINGLE: |
| |
64 case JABBER_X_DATA_JID_SINGLE: |
| |
65 { |
| |
66 const char *value = gaim_request_field_string_get_value(field); |
| |
67 fieldnode = xmlnode_new_child(result, "field"); |
| |
68 xmlnode_set_attrib(fieldnode, "var", id); |
| |
69 valuenode = xmlnode_new_child(fieldnode, "value"); |
| |
70 if(value) |
| |
71 xmlnode_insert_data(valuenode, value, -1); |
| |
72 break; |
| |
73 } |
| |
74 case JABBER_X_DATA_TEXT_MULTI: |
| |
75 { |
| |
76 char **pieces, **p; |
| |
77 const char *value = gaim_request_field_string_get_value(field); |
| |
78 fieldnode = xmlnode_new_child(result, "field"); |
| |
79 xmlnode_set_attrib(fieldnode, "var", id); |
| |
80 |
| |
81 pieces = g_strsplit(value, "\n", -1); |
| |
82 for(p = pieces; *p != NULL; p++) { |
| |
83 valuenode = xmlnode_new_child(fieldnode, "value"); |
| |
84 xmlnode_insert_data(valuenode, *p, -1); |
| |
85 } |
| |
86 g_strfreev(pieces); |
| |
87 } |
| |
88 break; |
| |
89 case JABBER_X_DATA_LIST_SINGLE: |
| |
90 case JABBER_X_DATA_LIST_MULTI: |
| |
91 { |
| |
92 const GList *selected = gaim_request_field_list_get_selected(field); |
| |
93 char *value; |
| |
94 fieldnode = xmlnode_new_child(result, "field"); |
| |
95 xmlnode_set_attrib(fieldnode, "var", id); |
| |
96 |
| |
97 while(selected) { |
| |
98 value = gaim_request_field_list_get_data(field, selected->data); |
| |
99 valuenode = xmlnode_new_child(fieldnode, "value"); |
| |
100 if(value) |
| |
101 xmlnode_insert_data(valuenode, value, -1); |
| |
102 selected = selected->next; |
| |
103 } |
| |
104 } |
| |
105 break; |
| |
106 case JABBER_X_DATA_BOOLEAN: |
| |
107 fieldnode = xmlnode_new_child(result, "field"); |
| |
108 xmlnode_set_attrib(fieldnode, "var", id); |
| |
109 valuenode = xmlnode_new_child(fieldnode, "value"); |
| |
110 if(gaim_request_field_bool_get_value(field)) |
| |
111 xmlnode_insert_data(valuenode, "1", -1); |
| |
112 else |
| |
113 xmlnode_insert_data(valuenode, "0", -1); |
| |
114 break; |
| |
115 case JABBER_X_DATA_IGNORE: |
| |
116 break; |
| |
117 } |
| |
118 } |
| |
119 } |
| |
120 |
| |
121 g_hash_table_destroy(data->fields); |
| |
122 while(data->values) { |
| |
123 g_free(data->values->data); |
| |
124 data->values = g_slist_delete_link(data->values, data->values); |
| |
125 } |
| |
126 g_free(data); |
| |
127 |
| |
128 cb(js, result, user_data); |
| |
129 } |
| |
130 |
| |
131 static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, GaimRequestFields *fields) { |
| |
132 xmlnode *result = xmlnode_new("x"); |
| |
133 jabber_x_data_cb cb = data->cb; |
| |
134 gpointer user_data = data->user_data; |
| |
135 JabberStream *js = data->js; |
| |
136 g_hash_table_destroy(data->fields); |
| |
137 while(data->values) { |
| |
138 g_free(data->values->data); |
| |
139 data->values = g_slist_delete_link(data->values, data->values); |
| |
140 } |
| |
141 g_free(data); |
| |
142 |
| |
143 xmlnode_set_namespace(result, "jabber:x:data"); |
| |
144 xmlnode_set_attrib(result, "type", "cancel"); |
| |
145 |
| |
146 cb(js, result, user_data); |
| |
147 } |
| |
148 |
| |
149 void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data) |
| |
150 { |
| |
151 void *handle; |
| |
152 xmlnode *fn, *x; |
| |
153 GaimRequestFields *fields; |
| |
154 GaimRequestFieldGroup *group; |
| |
155 GaimRequestField *field; |
| |
156 |
| |
157 char *title = NULL; |
| |
158 char *instructions = NULL; |
| |
159 |
| |
160 struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1); |
| |
161 |
| |
162 data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
| |
163 data->user_data = user_data; |
| |
164 data->cb = cb; |
| |
165 data->js = js; |
| |
166 |
| |
167 fields = gaim_request_fields_new(); |
| |
168 group = gaim_request_field_group_new(NULL); |
| |
169 gaim_request_fields_add_group(fields, group); |
| |
170 |
| |
171 for(fn = xmlnode_get_child(packet, "field"); fn; fn = xmlnode_get_next_twin(fn)) { |
| |
172 xmlnode *valuenode; |
| |
173 const char *type = xmlnode_get_attrib(fn, "type"); |
| |
174 const char *label = xmlnode_get_attrib(fn, "label"); |
| |
175 const char *var = xmlnode_get_attrib(fn, "var"); |
| |
176 char *value = NULL; |
| |
177 |
| |
178 if(!type) |
| |
179 continue; |
| |
180 |
| |
181 if(!var && strcmp(type, "fixed")) |
| |
182 continue; |
| |
183 if(!label) |
| |
184 label = var; |
| |
185 |
| |
186 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
187 value = xmlnode_get_data(valuenode); |
| |
188 |
| |
189 |
| |
190 /* XXX: handle <required/> */ |
| |
191 |
| |
192 if(!strcmp(type, "text-private")) { |
| |
193 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
194 value = xmlnode_get_data(valuenode); |
| |
195 |
| |
196 field = gaim_request_field_string_new(var, label, |
| |
197 value ? value : "", FALSE); |
| |
198 gaim_request_field_string_set_masked(field, TRUE); |
| |
199 gaim_request_field_group_add_field(group, field); |
| |
200 |
| |
201 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); |
| |
202 |
| |
203 if(value) |
| |
204 g_free(value); |
| |
205 } else if(!strcmp(type, "text-multi") || !strcmp(type, "jid-multi")) { |
| |
206 GString *str = g_string_new(""); |
| |
207 |
| |
208 for(valuenode = xmlnode_get_child(fn, "value"); valuenode; |
| |
209 valuenode = xmlnode_get_next_twin(valuenode)) { |
| |
210 |
| |
211 if(!(value = xmlnode_get_data(valuenode))) |
| |
212 continue; |
| |
213 |
| |
214 g_string_append_printf(str, "%s\n", value); |
| |
215 g_free(value); |
| |
216 } |
| |
217 |
| |
218 field = gaim_request_field_string_new(var, label, |
| |
219 str->str, TRUE); |
| |
220 gaim_request_field_group_add_field(group, field); |
| |
221 |
| |
222 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_MULTI)); |
| |
223 |
| |
224 g_string_free(str, TRUE); |
| |
225 } else if(!strcmp(type, "list-single") || !strcmp(type, "list-multi")) { |
| |
226 xmlnode *optnode; |
| |
227 GList *selected = NULL; |
| |
228 |
| |
229 field = gaim_request_field_list_new(var, label); |
| |
230 |
| |
231 if(!strcmp(type, "list-multi")) { |
| |
232 gaim_request_field_list_set_multi_select(field, TRUE); |
| |
233 g_hash_table_replace(data->fields, g_strdup(var), |
| |
234 GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI)); |
| |
235 } else { |
| |
236 g_hash_table_replace(data->fields, g_strdup(var), |
| |
237 GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE)); |
| |
238 } |
| |
239 |
| |
240 for(valuenode = xmlnode_get_child(fn, "value"); valuenode; |
| |
241 valuenode = xmlnode_get_next_twin(valuenode)) { |
| |
242 selected = g_list_prepend(selected, xmlnode_get_data(valuenode)); |
| |
243 } |
| |
244 |
| |
245 for(optnode = xmlnode_get_child(fn, "option"); optnode; |
| |
246 optnode = xmlnode_get_next_twin(optnode)) { |
| |
247 const char *lbl; |
| |
248 |
| |
249 if(!(valuenode = xmlnode_get_child(optnode, "value"))) |
| |
250 continue; |
| |
251 |
| |
252 if(!(value = xmlnode_get_data(valuenode))) |
| |
253 continue; |
| |
254 |
| |
255 if(!(lbl = xmlnode_get_attrib(optnode, "label"))) |
| |
256 label = value; |
| |
257 |
| |
258 data->values = g_slist_prepend(data->values, value); |
| |
259 |
| |
260 gaim_request_field_list_add(field, lbl, value); |
| |
261 if(g_list_find_custom(selected, value, (GCompareFunc)strcmp)) |
| |
262 gaim_request_field_list_add_selected(field, lbl); |
| |
263 } |
| |
264 gaim_request_field_group_add_field(group, field); |
| |
265 |
| |
266 while(selected) { |
| |
267 g_free(selected->data); |
| |
268 selected = g_list_delete_link(selected, selected); |
| |
269 } |
| |
270 |
| |
271 } else if(!strcmp(type, "boolean")) { |
| |
272 gboolean def = FALSE; |
| |
273 |
| |
274 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
275 value = xmlnode_get_data(valuenode); |
| |
276 |
| |
277 if(value && (!g_ascii_strcasecmp(value, "yes") || |
| |
278 !g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1"))) |
| |
279 def = TRUE; |
| |
280 |
| |
281 field = gaim_request_field_bool_new(var, label, def); |
| |
282 gaim_request_field_group_add_field(group, field); |
| |
283 |
| |
284 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN)); |
| |
285 |
| |
286 if(value) |
| |
287 g_free(value); |
| |
288 } else if(!strcmp(type, "fixed") && value) { |
| |
289 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
290 value = xmlnode_get_data(valuenode); |
| |
291 |
| |
292 field = gaim_request_field_label_new("", value); |
| |
293 gaim_request_field_group_add_field(group, field); |
| |
294 |
| |
295 if(value) |
| |
296 g_free(value); |
| |
297 } else if(!strcmp(type, "hidden")) { |
| |
298 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
299 value = xmlnode_get_data(valuenode); |
| |
300 |
| |
301 field = gaim_request_field_string_new(var, "", value ? value : "", |
| |
302 FALSE); |
| |
303 gaim_request_field_set_visible(field, FALSE); |
| |
304 gaim_request_field_group_add_field(group, field); |
| |
305 |
| |
306 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); |
| |
307 |
| |
308 if(value) |
| |
309 g_free(value); |
| |
310 } else { /* text-single, jid-single, and the default */ |
| |
311 if((valuenode = xmlnode_get_child(fn, "value"))) |
| |
312 value = xmlnode_get_data(valuenode); |
| |
313 |
| |
314 field = gaim_request_field_string_new(var, label, |
| |
315 value ? value : "", FALSE); |
| |
316 gaim_request_field_group_add_field(group, field); |
| |
317 |
| |
318 if(!strcmp(type, "jid-single")) { |
| |
319 gaim_request_field_set_type_hint(field, "screenname"); |
| |
320 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_JID_SINGLE)); |
| |
321 } else { |
| |
322 g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); |
| |
323 } |
| |
324 |
| |
325 if(value) |
| |
326 g_free(value); |
| |
327 } |
| |
328 } |
| |
329 |
| |
330 if((x = xmlnode_get_child(packet, "title"))) |
| |
331 title = xmlnode_get_data(x); |
| |
332 |
| |
333 if((x = xmlnode_get_child(packet, "instructions"))) |
| |
334 instructions = xmlnode_get_data(x); |
| |
335 |
| |
336 handle = gaim_request_fields(js->gc, title, title, instructions, fields, |
| |
337 _("OK"), G_CALLBACK(jabber_x_data_ok_cb), |
| |
338 _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb), data); |
| |
339 |
| |
340 if(title) |
| |
341 g_free(title); |
| |
342 if(instructions) |
| |
343 g_free(instructions); |
| |
344 |
| |
345 return handle; |
| |
346 } |
| |
347 |
| |
348 |