diff -r 000000000000 -r d0f3a028347a telepathygabble/src/search-mixin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telepathygabble/src/search-mixin.c Tue Feb 02 01:10:06 2010 +0200 @@ -0,0 +1,497 @@ +/* + * search-mixin.c - Source for GabbleSearchMixin + * Copyright (C) 2006 Collabora Ltd. + * + * @author Ole Andre Vadla Ravnaas + * @author Robert McQueen + * @author Senko Rasic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "loudmouth/loudmouth.h" +#include +#include +#include +#include +#include + +#include "telepathy-constants.h" +#include "telepathy-errors.h" + + +#include "debug.h" +#include "gabble-connection.h" +#include "namespaces.h" +#include "roster.h" +#include "util.h" + + +#include "search-mixin.h" +#include "search-mixin-signals-marshal.h" + + +#include "gabble_enums.h" + +#define _GNU_SOURCE /* Needed for strptime (_XOPEN_SOURCE can also be used). */ + +#define DEBUG_FLAG GABBLE_DEBUG_SEARCH + + +/* allocator */ + +#ifdef DEBUG_FLAG +//#define DEBUG(format, ...) +#define DEBUGGING 0 +#define NODE_DEBUG(n, s) +#endif /* DEBUG_FLAG */ + + +#ifdef EMULATOR +#include "libgabble_wsd_solution.h" + + GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_search_mixin,GQuark) + #define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_search_mixin, s)()) + + GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_search_mixin,GQuark) + #define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_search_mixin, s)()) + + GET_STATIC_VAR_FROM_TLS(alloc1,gabble_search_mixin,GabbleAllocator) + #define alloc1 (*GET_WSD_VAR_NAME(alloc1,gabble_search_mixin, s)()) + +#endif + +/* +Moved to gabble_enums.h +typedef struct _GabbleAllocator GabbleAllocator; +struct _GabbleAllocator +{ + gulong size; + guint limit; + guint count; +};*/ + + +typedef struct _IsKeyValidUserData IsKeyValidUserData; + +struct _IsKeyValidUserData +{ + GError **error; + gchar **search_key_names; + gboolean is_key_found; +}; + + +typedef struct _SearchKeyVarUserData SearchKeyVarUserData; + +struct _SearchKeyVarUserData +{ + LmMessageNode *x_node; + GabbleConnection *conn; + +}; + + +#define ga_new0(alloc, type) \ + ((type *) gabble_allocator_alloc0 (alloc)) + +static void +gabble_allocator_init (GabbleAllocator *alloc, gulong size, guint limit) +{ + g_assert (alloc != NULL); + g_assert (size > 0); + g_assert (limit > 0); + + alloc->size = size; + alloc->limit = limit; +} + +static gpointer gabble_allocator_alloc0 (GabbleAllocator *alloc) +{ + gpointer ret; + + g_assert (alloc != NULL); + g_assert (alloc->count <= alloc->limit); + + if (alloc->count == alloc->limit) + { + ret = NULL; + } + else + { + ret = g_malloc0 (alloc->size); + alloc->count++; + } + + return ret; +} + +static void gabble_allocator_free (GabbleAllocator *alloc, gpointer thing) +{ + g_assert (alloc != NULL); + g_assert (thing != NULL); + + g_free (thing); + alloc->count--; +} + + +/** + * gabble_search_mixin_class_get_offset_quark: + * + * Returns: the quark used for storing mixin offset on a GObjectClass + */ +GQuark +gabble_search_mixin_class_get_offset_quark () +{ +#ifndef EMULATOR + static GQuark offset_quark1 = 0; +#endif + + if (!offset_quark1) + offset_quark1 = g_quark_from_static_string("SearchMixinClassOffsetQuark"); + return offset_quark1; +} + +/** + * gabble_search_mixin_get_offset_quark: + * + * Returns: the quark used for storing mixin offset on a GObject + */ +GQuark +gabble_search_mixin_get_offset_quark () +{ +#ifndef EMULATOR + static GQuark offset_quark = 0; +#endif + + if (!offset_quark) + offset_quark = g_quark_from_static_string("SearchMixinOffsetQuark"); + return offset_quark; +} + + +/* GabbleSearchMixin */ +void +gabble_search_mixin_class_init (GObjectClass *obj_cls, glong offset) +{ + GabbleSearchMixinClass *mixin_cls; + + g_assert (G_IS_OBJECT_CLASS (obj_cls)); + + g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls), + GABBLE_SEARCH_MIXIN_CLASS_OFFSET_QUARK, + GINT_TO_POINTER (offset)); + + mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (obj_cls); + + + mixin_cls->search_result_received_signal_id = g_signal_new ("search-result-received", + G_OBJECT_CLASS_TYPE (obj_cls), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + search_mixin_marshal_VOID__UINT_BOXED, + G_TYPE_NONE, + 2, G_TYPE_UINT, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE )); + + mixin_cls->search_state_changed_signal_id = g_signal_new ("search-state-changed", + G_OBJECT_CLASS_TYPE (obj_cls), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT ); + +} + +void +gabble_search_mixin_init (GObject *obj, + glong offset ) +{ + GabbleSearchMixin *mixin; + + g_assert (G_IS_OBJECT (obj)); + + g_type_set_qdata (G_OBJECT_TYPE (obj), + GABBLE_SEARCH_MIXIN_OFFSET_QUARK, + GINT_TO_POINTER (offset)); + + mixin = GABBLE_SEARCH_MIXIN (obj); + + mixin->search_state = TP_CHANNEL_CONTACT_SEARCH_STATE_BEFORE; + + } + +void +gabble_search_mixin_finalize (GObject *obj) +{ + GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); + /* free any data held directly by the object here */ +} + +static void +setfield_foreach (gpointer key, gpointer value, gpointer user_data) +{ + const gchar *search_data_string = NULL; + SearchKeyVarUserData *key_var_struct = (SearchKeyVarUserData*)user_data; + LmMessageNode *field_node; + const gchar *search_key_var = NULL; + GType g_type = G_VALUE_TYPE (value); + + switch (g_type) + { + case G_TYPE_STRING: + search_data_string = g_value_get_string(value); + break; + default: + g_assert_not_reached (); + } + + search_key_var = (gchar *) g_hash_table_lookup (key_var_struct->conn->search_key_ht, + key /*Label*/); + field_node = lm_message_node_add_child ( key_var_struct->x_node, "field", NULL ); + lm_message_node_set_attribute ( field_node, "var", search_key_var ); + lm_message_node_add_child ( field_node, "value", search_data_string ); +} +static void +gabble_search_keynames_are_valid ( gpointer key, gpointer value, + gpointer userdata ) +{ + guint i; + const gchar *search_data_string = g_value_get_string(value); + IsKeyValidUserData *key_valid_struct = (IsKeyValidUserData*)userdata; + gchar **search_key_names = key_valid_struct->search_key_names; + + if( !key_valid_struct->is_key_found ) + return; + + if( key == NULL ) + { + key_valid_struct->is_key_found = FALSE; + return; + } + + + for (i = 0; search_key_names[i]; i++) + { + if (0 == strcmp (search_key_names[i], key)) + { + g_message("searchkey %s is valid\n",key); + if( search_data_string ) + g_message("value is %s\n",search_data_string); + return; + } + } + key_valid_struct->is_key_found = FALSE; + g_message("searchkey %s is invalid\n",key); + g_set_error ( key_valid_struct->error, TELEPATHY_ERRORS, InvalidArgument, + "invalid search key found : %s\n", key); +} + + +/** + * gabble_search_mixin_search + * + * Implements D-Bus method Search + * on interface org.freedesktop.Telepathy.Channel.Type.ContactSearch + * + * @error: Used to return a pointer to a GError detailing any error + * that occurred, D-Bus will throw the error only if this + * function returns false. + * + * Returns: TRUE if successful, FALSE if an error was thrown. + */ +gboolean gabble_search_mixin_search (GObject *obj,GHashTable *params, + GabbleConnection *conn, + GError **error) +{ + LmMessage *msg; + LmMessageNode *query_node; + LmMessageNode *x_node; + gboolean result; + const gchar *service = NULL; + GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); + IsKeyValidUserData *user_data = NULL; + SearchKeyVarUserData *get_keyvar_userdata = NULL; + + + if (params == NULL) + { + g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, + "invalid argument \n"); + return FALSE; + } + + if ( !g_hash_table_size(params) ) + { + g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, + "invalid argument, no key-value pair to do search\n"); + return FALSE; + } + + service = conn->search_service_jid; + + if (service == NULL) + { + g_set_error (error, TELEPATHY_ERRORS, NotAvailable, + "Search Service is not available\n"); + return FALSE; + } + + g_message("service is %s\n",service); + + if (conn->search_key_names == NULL) + { + g_set_error (error, TELEPATHY_ERRORS, NotAvailable, + "search key names not available"); + return FALSE; + } + + user_data = g_new0 (IsKeyValidUserData, 1); + user_data->is_key_found = TRUE; + user_data->error = error; + user_data->search_key_names = conn->search_key_names; + + g_hash_table_foreach (params, gabble_search_keynames_are_valid, user_data ); + + if(!user_data->is_key_found) + { + g_free(user_data); + g_message("invalid searchkey found\n"); + return FALSE; + } + + g_free(user_data); + + msg= lm_message_new_with_sub_type ( service, + LM_MESSAGE_TYPE_IQ, + LM_MESSAGE_SUB_TYPE_SET); + query_node = lm_message_node_add_child (msg->node, "query", NULL); + + lm_message_node_set_attribute (query_node, "xmlns", NS_SEARCH); + + x_node = lm_message_node_add_child ( query_node, "x", NULL ); + + lm_message_node_set_attributes (x_node, + "xmlns", NS_X_DATA, + "type", "submit", + NULL); + + get_keyvar_userdata = g_new0 (SearchKeyVarUserData, 1); + get_keyvar_userdata->x_node = x_node; + get_keyvar_userdata->conn = conn; + + g_hash_table_foreach (params, setfield_foreach, get_keyvar_userdata ); + + g_free(get_keyvar_userdata); + + result = _gabble_connection_send (conn, msg, error); + lm_message_unref (msg); + + if (!result) + return FALSE; + + //this means for each search attempt, a new channel should be created + mixin->search_state = TP_CHANNEL_CONTACT_SEARCH_STATE_DURING; + + //send search state changed signal if required + _gabble_search_mixin_emit_search_state_changed(obj,mixin->search_state); + + return TRUE; +} + +gboolean gabble_search_mixin_get_search_state ( GObject *obj, + guint *ret, + GError **error ) +{ + GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); + *ret = mixin->search_state; + return TRUE; +} + + +/** + * gabble_search_mixin_get_search_keys + * + * Implements D-Bus method GetSearchKeys + * on interface org.freedesktop.Telepathy.Channel.Type.ContactSearch + * + * @error: Used to return a pointer to a GError detailing any error + * that occurred, D-Bus will throw the error only if this + * function returns false. + * + * Returns: TRUE if successful, FALSE if an error was thrown. + */ +gboolean +gabble_search_mixin_get_search_keys ( GObject *obj, + gchar **ret_instruction, + gchar ***ret_searchkeys, + GabbleConnection *conn, + GError **error + ) +{ + //later this method should be modified to give + //types of search keys fields also + + if (conn->search_key_names == NULL) + { + g_set_error (error, TELEPATHY_ERRORS, NotAvailable, + "search keys not available"); + return FALSE; + } + + *ret_searchkeys = g_strdupv ((gchar **) conn->search_key_names); + *ret_instruction = g_strdup ( (gchar*)conn->search_instr); + + g_message("conn->search_instr :%s\n",(gchar*)conn->search_instr); + g_message("ret_instruction :%s\n",(gchar*)*ret_instruction ); + + return TRUE; +} + + +void +_gabble_search_mixin_emit_search_result_received (GObject *obj, + guint contact_handle, + GHashTable *values ) +{ + GabbleSearchMixinClass *mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (G_OBJECT_GET_CLASS + (obj)); + + g_signal_emit (obj, mixin_cls->search_result_received_signal_id, 0, + contact_handle, + values ); +} + +void +_gabble_search_mixin_emit_search_state_changed (GObject *obj, + guint search_state ) +{ + GabbleSearchMixinClass *mixin_cls = GABBLE_SEARCH_MIXIN_CLASS (G_OBJECT_GET_CLASS + (obj)); + g_signal_emit (obj, mixin_cls->search_state_changed_signal_id, 0, + search_state ); +} + +void +_gabble_search_mixin_set_search_state (GObject *obj, guint state ) +{ + GabbleSearchMixin *mixin = GABBLE_SEARCH_MIXIN (obj); + mixin->search_state = state; +} + +