telepathygabble/src/search-mixin.c
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:10:06 +0200
changeset 0 d0f3a028347a
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
 * search-mixin.c - Source for GabbleSearchMixin
 * Copyright (C) 2006 Collabora Ltd.
 * 
 *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
 *   @author Robert McQueen <robert.mcqueen@collabora.co.uk>
 *   @author Senko Rasic <senko@senko.net>
 *
 * 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 <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#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;
}