1 /* |
|
2 * search-mixin.c - Source for GabbleSearchMixin |
|
3 * Copyright (C) 2006 Collabora Ltd. |
|
4 * |
|
5 * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> |
|
6 * @author Robert McQueen <robert.mcqueen@collabora.co.uk> |
|
7 * @author Senko Rasic <senko@senko.net> |
|
8 * |
|
9 * This library is free software; you can redistribute it and/or |
|
10 * modify it under the terms of the GNU Lesser General Public |
|
11 * License as published by the Free Software Foundation; either |
|
12 * version 2.1 of the License, or (at your option) any later version. |
|
13 * |
|
14 * This library is distributed in the hope that it will be useful, |
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 * Lesser General Public License for more details. |
|
18 * |
|
19 * You should have received a copy of the GNU Lesser General Public |
|
20 * License along with this library; if not, write to the Free Software |
|
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
22 */ |
|
23 |
|
24 |
|
25 #include "loudmouth/loudmouth.h" |
|
26 #include <dbus/dbus-glib.h> |
|
27 #include <stdio.h> |
|
28 #include <stdlib.h> |
|
29 #include <string.h> |
|
30 #include <time.h> |
|
31 |
|
32 #include "telepathy-constants.h" |
|
33 #include "telepathy-errors.h" |
|
34 |
|
35 |
|
36 #include "debug.h" |
|
37 #include "gabble-connection.h" |
|
38 #include "namespaces.h" |
|
39 #include "roster.h" |
|
40 #include "util.h" |
|
41 |
|
42 |
|
43 #include "search-mixin.h" |
|
44 #include "search-mixin-signals-marshal.h" |
|
45 |
|
46 |
|
47 #include "gabble_enums.h" |
|
48 |
|
49 #define _GNU_SOURCE /* Needed for strptime (_XOPEN_SOURCE can also be used). */ |
|
50 |
|
51 #define DEBUG_FLAG GABBLE_DEBUG_SEARCH |
|
52 |
|
53 |
|
54 /* allocator */ |
|
55 |
|
56 #ifdef DEBUG_FLAG |
|
57 //#define DEBUG(format, ...) |
|
58 #define DEBUGGING 0 |
|
59 #define NODE_DEBUG(n, s) |
|
60 #endif /* DEBUG_FLAG */ |
|
61 |
|
62 |
|
63 #ifdef EMULATOR |
|
64 #include "libgabble_wsd_solution.h" |
|
65 |
|
66 GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_search_mixin,GQuark) |
|
67 #define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_search_mixin, s)()) |
|
68 |
|
69 GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_search_mixin,GQuark) |
|
70 #define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_search_mixin, s)()) |
|
71 |
|
72 GET_STATIC_VAR_FROM_TLS(alloc1,gabble_search_mixin,GabbleAllocator) |
|
73 #define alloc1 (*GET_WSD_VAR_NAME(alloc1,gabble_search_mixin, s)()) |
|
74 |
|
75 #endif |
|
76 |
|
77 /* |
|
78 Moved to gabble_enums.h |
|
79 typedef struct _GabbleAllocator GabbleAllocator; |
|
80 struct _GabbleAllocator |
|
81 { |
|
82 gulong size; |
|
83 guint limit; |
|
84 guint count; |
|
85 };*/ |
|
86 |
|
87 |
|
88 typedef struct _IsKeyValidUserData IsKeyValidUserData; |
|
89 |
|
90 struct _IsKeyValidUserData |
|
91 { |
|
92 GError **error; |
|
93 gchar **search_key_names; |
|
94 gboolean is_key_found; |
|
95 }; |
|
96 |
|
97 |
|
98 typedef struct _SearchKeyVarUserData SearchKeyVarUserData; |
|
99 |
|
100 struct _SearchKeyVarUserData |
|
101 { |
|
102 LmMessageNode *x_node; |
|
103 GabbleConnection *conn; |
|
104 |
|
105 }; |
|
106 |
|
107 |
|
108 #define ga_new0(alloc, type) \ |
|
109 ((type *) gabble_allocator_alloc0 (alloc)) |
|
110 |
|
111 static void |
|
112 gabble_allocator_init (GabbleAllocator *alloc, gulong size, guint limit) |
|
113 { |
|
114 g_assert (alloc != NULL); |
|
115 g_assert (size > 0); |
|
116 g_assert (limit > 0); |
|
117 |
|
118 alloc->size = size; |
|
119 alloc->limit = limit; |
|
120 } |
|
121 |
|
122 static gpointer gabble_allocator_alloc0 (GabbleAllocator *alloc) |
|
123 { |
|
124 gpointer ret; |
|
125 |
|
126 g_assert (alloc != NULL); |
|
127 g_assert (alloc->count <= alloc->limit); |
|
128 |
|
129 if (alloc->count == alloc->limit) |
|
130 { |
|
131 ret = NULL; |
|
132 } |
|
133 else |
|
134 { |
|
135 ret = g_malloc0 (alloc->size); |
|
136 alloc->count++; |
|
137 } |
|
138 |
|
139 return ret; |
|
140 } |
|
141 |
|
142 static void gabble_allocator_free (GabbleAllocator *alloc, gpointer thing) |
|
143 { |
|
144 g_assert (alloc != NULL); |
|
145 g_assert (thing != NULL); |
|
146 |
|
147 g_free (thing); |
|
148 alloc->count--; |
|
149 } |
|
150 |
|
151 |
|
152 /** |
|
153 * gabble_search_mixin_class_get_offset_quark: |
|
154 * |
|
155 * Returns: the quark used for storing mixin offset on a GObjectClass |
|
156 */ |
|
157 GQuark |
|
158 gabble_search_mixin_class_get_offset_quark () |
|
159 { |
|
160 #ifndef EMULATOR |
|
161 static GQuark offset_quark1 = 0; |
|
162 #endif |
|
163 |
|
164 if (!offset_quark1) |
|
165 offset_quark1 = g_quark_from_static_string("SearchMixinClassOffsetQuark"); |
|
166 return offset_quark1; |
|
167 } |
|
168 |
|
169 /** |
|
170 * gabble_search_mixin_get_offset_quark: |
|
171 * |
|
172 * Returns: the quark used for storing mixin offset on a GObject |
|
173 */ |
|
174 GQuark |
|
175 gabble_search_mixin_get_offset_quark () |
|
176 { |
|
177 #ifndef EMULATOR |
|
178 static GQuark offset_quark = 0; |
|
179 #endif |
|
180 |
|
181 if (!offset_quark) |
|
182 offset_quark = g_quark_from_static_string("SearchMixinOffsetQuark"); |
|
183 return offset_quark; |
|
184 } |
|
185 |
|
186 |
|
187 /* GabbleSearchMixin */ |
|
188 void |
|
189 gabble_search_mixin_class_init (GObjectClass *obj_cls, glong offset) |
|
190 { |
|
191 GabbleSearchMixinClass *mixin_cls; |
|
192 |
|
193 g_assert (G_IS_OBJECT_CLASS (obj_cls)); |
|
194 |
|
195 g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls), |
|
196 GABBLE_SEARCH_MIXIN_CLASS_OFFSET_QUARK, |
|
197 GINT_TO_POINTER (offset)); |
|
198 |
|
199 mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (obj_cls); |
|
200 |
|
201 |
|
202 mixin_cls->search_result_received_signal_id = g_signal_new ("search-result-received", |
|
203 G_OBJECT_CLASS_TYPE (obj_cls), |
|
204 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
205 0, |
|
206 NULL, NULL, |
|
207 search_mixin_marshal_VOID__UINT_BOXED, |
|
208 G_TYPE_NONE, |
|
209 2, G_TYPE_UINT, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE )); |
|
210 |
|
211 mixin_cls->search_state_changed_signal_id = g_signal_new ("search-state-changed", |
|
212 G_OBJECT_CLASS_TYPE (obj_cls), |
|
213 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, |
|
214 0, |
|
215 NULL, NULL, |
|
216 g_cclosure_marshal_VOID__UINT, |
|
217 G_TYPE_NONE, 1, G_TYPE_UINT ); |
|
218 |
|
219 } |
|
220 |
|
221 void |
|
222 gabble_search_mixin_init (GObject *obj, |
|
223 glong offset ) |
|
224 { |
|
225 GabbleSearchMixin *mixin; |
|
226 |
|
227 g_assert (G_IS_OBJECT (obj)); |
|
228 |
|
229 g_type_set_qdata (G_OBJECT_TYPE (obj), |
|
230 GABBLE_SEARCH_MIXIN_OFFSET_QUARK, |
|
231 GINT_TO_POINTER (offset)); |
|
232 |
|
233 mixin = GABBLE_SEARCH_MIXIN (obj); |
|
234 |
|
235 mixin->search_state = TP_CHANNEL_CONTACT_SEARCH_STATE_BEFORE; |
|
236 |
|
237 } |
|
238 |
|
239 void |
|
240 gabble_search_mixin_finalize (GObject *obj) |
|
241 { |
|
242 GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); |
|
243 /* free any data held directly by the object here */ |
|
244 } |
|
245 |
|
246 static void |
|
247 setfield_foreach (gpointer key, gpointer value, gpointer user_data) |
|
248 { |
|
249 const gchar *search_data_string = NULL; |
|
250 SearchKeyVarUserData *key_var_struct = (SearchKeyVarUserData*)user_data; |
|
251 LmMessageNode *field_node; |
|
252 const gchar *search_key_var = NULL; |
|
253 GType g_type = G_VALUE_TYPE (value); |
|
254 |
|
255 switch (g_type) |
|
256 { |
|
257 case G_TYPE_STRING: |
|
258 search_data_string = g_value_get_string(value); |
|
259 break; |
|
260 default: |
|
261 g_assert_not_reached (); |
|
262 } |
|
263 |
|
264 search_key_var = (gchar *) g_hash_table_lookup (key_var_struct->conn->search_key_ht, |
|
265 key /*Label*/); |
|
266 field_node = lm_message_node_add_child ( key_var_struct->x_node, "field", NULL ); |
|
267 lm_message_node_set_attribute ( field_node, "var", search_key_var ); |
|
268 lm_message_node_add_child ( field_node, "value", search_data_string ); |
|
269 } |
|
270 static void |
|
271 gabble_search_keynames_are_valid ( gpointer key, gpointer value, |
|
272 gpointer userdata ) |
|
273 { |
|
274 guint i; |
|
275 const gchar *search_data_string = g_value_get_string(value); |
|
276 IsKeyValidUserData *key_valid_struct = (IsKeyValidUserData*)userdata; |
|
277 gchar **search_key_names = key_valid_struct->search_key_names; |
|
278 |
|
279 if( !key_valid_struct->is_key_found ) |
|
280 return; |
|
281 |
|
282 if( key == NULL ) |
|
283 { |
|
284 key_valid_struct->is_key_found = FALSE; |
|
285 return; |
|
286 } |
|
287 |
|
288 |
|
289 for (i = 0; search_key_names[i]; i++) |
|
290 { |
|
291 if (0 == strcmp (search_key_names[i], key)) |
|
292 { |
|
293 g_message("searchkey %s is valid\n",key); |
|
294 if( search_data_string ) |
|
295 g_message("value is %s\n",search_data_string); |
|
296 return; |
|
297 } |
|
298 } |
|
299 key_valid_struct->is_key_found = FALSE; |
|
300 g_message("searchkey %s is invalid\n",key); |
|
301 g_set_error ( key_valid_struct->error, TELEPATHY_ERRORS, InvalidArgument, |
|
302 "invalid search key found : %s\n", key); |
|
303 } |
|
304 |
|
305 |
|
306 /** |
|
307 * gabble_search_mixin_search |
|
308 * |
|
309 * Implements D-Bus method Search |
|
310 * on interface org.freedesktop.Telepathy.Channel.Type.ContactSearch |
|
311 * |
|
312 * @error: Used to return a pointer to a GError detailing any error |
|
313 * that occurred, D-Bus will throw the error only if this |
|
314 * function returns false. |
|
315 * |
|
316 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
317 */ |
|
318 gboolean gabble_search_mixin_search (GObject *obj,GHashTable *params, |
|
319 GabbleConnection *conn, |
|
320 GError **error) |
|
321 { |
|
322 LmMessage *msg; |
|
323 LmMessageNode *query_node; |
|
324 LmMessageNode *x_node; |
|
325 gboolean result; |
|
326 const gchar *service = NULL; |
|
327 GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); |
|
328 IsKeyValidUserData *user_data = NULL; |
|
329 SearchKeyVarUserData *get_keyvar_userdata = NULL; |
|
330 |
|
331 |
|
332 if (params == NULL) |
|
333 { |
|
334 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
335 "invalid argument \n"); |
|
336 return FALSE; |
|
337 } |
|
338 |
|
339 if ( !g_hash_table_size(params) ) |
|
340 { |
|
341 g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, |
|
342 "invalid argument, no key-value pair to do search\n"); |
|
343 return FALSE; |
|
344 } |
|
345 |
|
346 service = conn->search_service_jid; |
|
347 |
|
348 if (service == NULL) |
|
349 { |
|
350 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
351 "Search Service is not available\n"); |
|
352 return FALSE; |
|
353 } |
|
354 |
|
355 g_message("service is %s\n",service); |
|
356 |
|
357 if (conn->search_key_names == NULL) |
|
358 { |
|
359 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
360 "search key names not available"); |
|
361 return FALSE; |
|
362 } |
|
363 |
|
364 user_data = g_new0 (IsKeyValidUserData, 1); |
|
365 user_data->is_key_found = TRUE; |
|
366 user_data->error = error; |
|
367 user_data->search_key_names = conn->search_key_names; |
|
368 |
|
369 g_hash_table_foreach (params, gabble_search_keynames_are_valid, user_data ); |
|
370 |
|
371 if(!user_data->is_key_found) |
|
372 { |
|
373 g_free(user_data); |
|
374 g_message("invalid searchkey found\n"); |
|
375 return FALSE; |
|
376 } |
|
377 |
|
378 g_free(user_data); |
|
379 |
|
380 msg= lm_message_new_with_sub_type ( service, |
|
381 LM_MESSAGE_TYPE_IQ, |
|
382 LM_MESSAGE_SUB_TYPE_SET); |
|
383 query_node = lm_message_node_add_child (msg->node, "query", NULL); |
|
384 |
|
385 lm_message_node_set_attribute (query_node, "xmlns", NS_SEARCH); |
|
386 |
|
387 x_node = lm_message_node_add_child ( query_node, "x", NULL ); |
|
388 |
|
389 lm_message_node_set_attributes (x_node, |
|
390 "xmlns", NS_X_DATA, |
|
391 "type", "submit", |
|
392 NULL); |
|
393 |
|
394 get_keyvar_userdata = g_new0 (SearchKeyVarUserData, 1); |
|
395 get_keyvar_userdata->x_node = x_node; |
|
396 get_keyvar_userdata->conn = conn; |
|
397 |
|
398 g_hash_table_foreach (params, setfield_foreach, get_keyvar_userdata ); |
|
399 |
|
400 g_free(get_keyvar_userdata); |
|
401 |
|
402 result = _gabble_connection_send (conn, msg, error); |
|
403 lm_message_unref (msg); |
|
404 |
|
405 if (!result) |
|
406 return FALSE; |
|
407 |
|
408 //this means for each search attempt, a new channel should be created |
|
409 mixin->search_state = TP_CHANNEL_CONTACT_SEARCH_STATE_DURING; |
|
410 |
|
411 //send search state changed signal if required |
|
412 _gabble_search_mixin_emit_search_state_changed(obj,mixin->search_state); |
|
413 |
|
414 return TRUE; |
|
415 } |
|
416 |
|
417 gboolean gabble_search_mixin_get_search_state ( GObject *obj, |
|
418 guint *ret, |
|
419 GError **error ) |
|
420 { |
|
421 GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); |
|
422 *ret = mixin->search_state; |
|
423 return TRUE; |
|
424 } |
|
425 |
|
426 |
|
427 /** |
|
428 * gabble_search_mixin_get_search_keys |
|
429 * |
|
430 * Implements D-Bus method GetSearchKeys |
|
431 * on interface org.freedesktop.Telepathy.Channel.Type.ContactSearch |
|
432 * |
|
433 * @error: Used to return a pointer to a GError detailing any error |
|
434 * that occurred, D-Bus will throw the error only if this |
|
435 * function returns false. |
|
436 * |
|
437 * Returns: TRUE if successful, FALSE if an error was thrown. |
|
438 */ |
|
439 gboolean |
|
440 gabble_search_mixin_get_search_keys ( GObject *obj, |
|
441 gchar **ret_instruction, |
|
442 gchar ***ret_searchkeys, |
|
443 GabbleConnection *conn, |
|
444 GError **error |
|
445 ) |
|
446 { |
|
447 //later this method should be modified to give |
|
448 //types of search keys fields also |
|
449 |
|
450 if (conn->search_key_names == NULL) |
|
451 { |
|
452 g_set_error (error, TELEPATHY_ERRORS, NotAvailable, |
|
453 "search keys not available"); |
|
454 return FALSE; |
|
455 } |
|
456 |
|
457 *ret_searchkeys = g_strdupv ((gchar **) conn->search_key_names); |
|
458 *ret_instruction = g_strdup ( (gchar*)conn->search_instr); |
|
459 |
|
460 g_message("conn->search_instr :%s\n",(gchar*)conn->search_instr); |
|
461 g_message("ret_instruction :%s\n",(gchar*)*ret_instruction ); |
|
462 |
|
463 return TRUE; |
|
464 } |
|
465 |
|
466 |
|
467 void |
|
468 _gabble_search_mixin_emit_search_result_received (GObject *obj, |
|
469 guint contact_handle, |
|
470 GHashTable *values ) |
|
471 { |
|
472 GabbleSearchMixinClass *mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (G_OBJECT_GET_CLASS |
|
473 (obj)); |
|
474 |
|
475 g_signal_emit (obj, mixin_cls->search_result_received_signal_id, 0, |
|
476 contact_handle, |
|
477 values ); |
|
478 } |
|
479 |
|
480 void |
|
481 _gabble_search_mixin_emit_search_state_changed (GObject *obj, |
|
482 guint search_state ) |
|
483 { |
|
484 GabbleSearchMixinClass *mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (G_OBJECT_GET_CLASS |
|
485 (obj)); |
|
486 g_signal_emit (obj, mixin_cls->search_state_changed_signal_id, 0, |
|
487 search_state ); |
|
488 } |
|
489 |
|
490 void |
|
491 _gabble_search_mixin_set_search_state (GObject *obj, guint state ) |
|
492 { |
|
493 GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); |
|
494 mixin->search_state = state; |
|
495 } |
|
496 |
|
497 |
|