/***************************************************************************
                                core-conn.c
                                -----------
    begin                : Sat Oct 11 2003
    copyright            : (C) 2003 by Tim-Philipp Müller, Axel C.
    email                : t.i.m at orange dot net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


/* This is a reimplementation of the old gui-core connection protocol into a
 * a GObject framework. Lots of crack ahead.
 *									-axel
 */



#include <glib-object.h>
#include <gnet.h>

#include "core-conn.h"
#include "dns_lookup.h"
#include "gui-marshal.h"
#include "misc.h"
#include "options.h"
#include "gui_core_protocol.h"
#include "packet.h"
#include "ramfile.h"
#include "tag.h"


#include <string.h>
#include <stdio.h>
#include <time.h> 

#define CONN_CONNECT_TIMEOUT  10*1000

/* If we haven't received data from
 *  the core for 90s, a timeout
 *  function is called */
#define CONN_TIMEOUT_INITIAL  15*1000
#define CONN_TIMEOUT          90*1000


#define  gui_core_conn_send_single_command(c,cmd)   gui_core_conn_send_command_with_data(c,cmd,NULL,0)


/* global variables */

GuiCoreConn *core = NULL;


/* private enums and structures */

/* signal enums */

enum
{
	CONNECTED,
	DISCONNECTED,
	CORE_CONN_STATUS,
	COPTIONS,
	OVERNET_STATUS,
	EDONKEY_STATUS,
	HYBRID_STATUS,
	DOWNLOAD_STATUS,
	DOWNLOAD_GAPS,
	SPEED_TOTAL,
	NEW_DOWNLOAD,
	REMOVE_DOWNLOAD,
	SHARED_FILE_HASHING,
	SHARED_DIRS,
	SHARED_FILES,
	UPLOAD_STATUS,
	REMOVE_UPLOAD_SLOT,
	CHANGE_UPLOAD_SLOT,
	STATUS_MESSAGE,
	STATUS_MESSAGE_UNFILTERED,
	ERROR_MESSAGE,
	CLIENT_STATS,
	SERVER_CONNECTED,
	SERVER_CONNECT_FAILED,
	SERVER_DISCONNECTED,
	SERVER_LIST,
	SERVER_STATS,
	SEARCH_EXTENDED,
	SEARCH_RESULTS,
	LAST_SIGNAL
};


struct _GuiCoreConnPrivate
{
	ramfile     *packet;
	guint        packetlen; /* length of packet payload (without header) */
	gboolean     got_header;

	GConn       *guicoreconn;

	gchar       *host;
	guint        port;
	gchar       *username;
	gchar       *password;

	guint        conn_status;

	gboolean     closed_deliberately;
	gboolean     got_options;  /* have we gotten the core options yet?                    */
	gboolean     got_data;     /* have we gotten any valid data at all yet from the core? */
	gboolean     got_stats;    /* have we gotten an download-/upload-stats packet yet?    */

	gboolean     is_localhost;
	gboolean     is_overnet;
	gboolean     is_edonkey;
	gboolean     is_hybrid;

	gfloat       current_dl_speed;
	gfloat       current_ul_speed;

	CoreOptions  coreopts;

	guint        reqstats_timeout;
	guint        task_timeout;     /* timeout for handler called every minute for tasks */
	guint        minutes;          /* number of minutes been in status COMMUNICATING    */
};

struct _GuiCoreConnMsgHandler
{
	guint8       cmd;
  gboolean   (*handler) (GuiCoreConn *conn, ramfile *packet);
	const gchar *cmdstr;
	guint        count;
	guint        payload;
};


/* local functions */

static void          gui_core_conn_class_init        (GuiCoreConnClass *klass);

static void          gui_core_conn_instance_init     (GuiCoreConn *model);

static void          gui_core_conn_finalize          (GObject *object);

/* message handler functions */

static gboolean      onDeprecatedMessage   (GuiCoreConn *conn, ramfile *packet);

static gboolean      onCoreOptions         (GuiCoreConn *conn, ramfile *packet);

static gboolean      onDownloadStatus      (GuiCoreConn *conn, ramfile *packet);

static gboolean      onDownloadGaps        (GuiCoreConn *conn, ramfile *packet);

static gboolean      onUploadStatus        (GuiCoreConn *conn, ramfile *packet);

static gboolean      onRemoveUploadSlot    (GuiCoreConn *conn, ramfile *packet);

static gboolean      onChangeUploadSlot    (GuiCoreConn *conn, ramfile *packet);

static gboolean      onNewDownload         (GuiCoreConn *conn, ramfile *packet);

static gboolean      onRemoveDownload      (GuiCoreConn *conn, ramfile *packet);

static gboolean      onHashing             (GuiCoreConn *conn, ramfile *packet);

static gboolean      onSharedDirs          (GuiCoreConn *conn, ramfile *packet);

static gboolean      onSharedFiles         (GuiCoreConn *conn, ramfile *packet);

static gboolean      onStatusMessage       (GuiCoreConn *conn, ramfile *packet);

static gboolean      onErrorMessage        (GuiCoreConn *conn, ramfile *packet);

static gboolean      onClientStats         (GuiCoreConn *conn, ramfile *packet);

static gboolean      onServerConnected     (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onServerDisconnected  (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onServerList          (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onServerStats         (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onExtendingSearch     (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onSearchResult        (GuiCoreConn *conn, ramfile *packet);

static gboolean      onSearchResults       (GuiCoreConn *conn, ramfile *packet); /* eDonkey only */

static gboolean      onTimeoutRequestStats (GuiCoreConn *conn);

static gboolean      onTaskTimeout         (GuiCoreConn *conn);

static void          parse_packet          (GuiCoreConn *conn);

static void          send_packet           (GuiCoreConn *conn, ramfile *packet);


/* local variables */

static guint                   core_conn_signals[LAST_SIGNAL];  /* all 0 */

static GObjectClass           *parent_class;                        /* NULL */

struct _GuiCoreConnMsgHandler  msg_handlers[] =
	{
	/* put the two most frequent ones at the beginning (and yes I know
	*  that the '&' in front of the function name is not necessary :P */
	{ CORE_DOWNLOAD_STATUS      , &onDownloadStatus    , "CoreDownloadStatus"     , 0, 0 },
	{ CORE_UPLOAD_SLOT_STATUS   , &onUploadStatus      , "CoreUploadSlotStatus"   , 0, 0 },
	{ CORE_DOWNLOAD_GAP_DETAILS , &onDownloadGaps      , "CoreDownloadGapDetails" , 0, 0 },
	{ CORE_SERVER_LIST          , &onServerList        , "CoreServerList"         , 0, 0 },
	{ CORE_FRIEND_LIST          , NULL                 , "CoreFriendList"         , 0, 0 },
	{ CORE_SHARED_DIRS          , &onSharedDirs        , "CoreSharedDirs"         , 0, 0 },
	{ CORE_SHARED_FILES         , &onSharedFiles       , "CoreSharedFiles"        , 0, 0 },
	{ CORE_CLIENT_STATS         , &onClientStats       , "CoreClientStats"        , 0, 0 },
	{ CORE_STATUS_MSG           , &onStatusMessage     , "CoreStatusMessage"      , 0, 0 },
	{ CORE_ERROR_MSG            , &onErrorMessage      , "CoreErrorMessage"       , 0, 0 },
	{ CORE_CONNECTED_TO         , &onServerConnected   , "CoreConnectedTo"        , 0, 0 },
	{ CORE_DISCONNECTED         , &onServerDisconnected, "CoreDisconnected"       , 0, 0 },
	{ CORE_SET_SERVER_STATS     , &onServerStats       , "CoreSetServerStats"     , 0, 0 },
	{ CORE_EXTENDING_SEARCH     , &onExtendingSearch   , "CoreExtendingSearch"    , 0, 0 },

	{ CORE_SEARCH_RESULT        , &onSearchResult      , "CoreSearchResult"       , 0, 0 },
	{ CORE_ED_SEARCH_RESULT     , &onSearchResult      , "CoreEdSearchResult"     , 0, 0 }, /* cdonkey only */
	{ CORE_NEW_SEARCH_RESULTS   , &onSearchResults     , "CoreNewSearchResults"   , 0, 0 },
	{ CORE_ED_NEW_SEARCH_RESULT , &onSearchResults     , "CoreEdSearchResults"    , 0, 0 }, /* cdonkey only */
	{ CORE_NEW_DOWNLOAD         , &onNewDownload       , "CoreNewDownload"        , 0, 0 },
	{ CORE_REMOVE_DOWNLOAD      , &onRemoveDownload    , "CoreRemoveDownload"     , 0, 0 },
	{ CORE_REMOVE_UPLOAD_SLOT   , &onRemoveUploadSlot  , "CoreRemoveUploadSlot"   , 0, 0 },
	{ CORE_USER_FILES           , NULL                 , "CoreUserFiles"          , 0, 0 },
	{ CORE_HASHING              , &onHashing           , "CoreHashing"            , 0, 0 },
	{ CORE_FRIEND_LIST_UPDATE   , NULL                 , "CoreFriendListUpdate"   , 0, 0 },
	{ CORE_OPTIONS              , &onCoreOptions       , "CoreOptions"            , 0, 0 },
	{ CORE_CHANGE_UPLOAD_SLOT   , &onChangeUploadSlot  , "CoreChangeUploadSlot"   , 0, 0 },
	{ CORE_NEW_UPLOAD           , &onDeprecatedMessage , "[CoreNewUpload]"        , 0, 0 },
	{ CORE_REMOVE_UPLOAD        , &onDeprecatedMessage , "[CoreRemoveUpload]"     , 0, 0 },
	{ CORE_NEW_UPLOAD_SLOT      , &onDeprecatedMessage , "[CoreNewUploadSlot]"    , 0, 0 },
	{ CORE_UPLOAD_STATUS        , &onDeprecatedMessage , "[CoreUploadStatus]"     , 0, 0 },
	{ 0, NULL, NULL, 0 }
};


/***************************************************************************
 *
 *   gui_core_conn_class_init
 *
 ***************************************************************************/

static void
gui_core_conn_class_init (GuiCoreConnClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	core_conn_signals[CONNECTED] =
		g_signal_new ("connected",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, connected),
                              NULL, NULL,
		                          gui_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);

	core_conn_signals[CORE_CONN_STATUS] =
		g_signal_new ("core-conn-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, core_conn_status),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT,
                              G_TYPE_NONE, 1, G_TYPE_UINT);

	core_conn_signals[DISCONNECTED] =
		g_signal_new ("disconnected",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, disconnected),
                              NULL, NULL,
		                          gui_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);

	core_conn_signals[COPTIONS] =
		g_signal_new ("core-options",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, core_options),
                              NULL, NULL,
		                          gui_marshal_VOID__POINTER,
                              G_TYPE_NONE, 1, G_TYPE_POINTER);

	core_conn_signals[OVERNET_STATUS] =
		g_signal_new ("overnet-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, overnet_status),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING_STRING,
                              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);

	core_conn_signals[HYBRID_STATUS] =
		g_signal_new ("hybrid-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, hybrid_status),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING_STRING,
                              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);

	core_conn_signals[EDONKEY_STATUS] =
		g_signal_new ("edonkey-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, edonkey_status),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_STRING,
                              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);

	core_conn_signals[DOWNLOAD_STATUS] =
		g_signal_new ("download-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, download_status),
		                          NULL, NULL,
		                          gui_marshal_VOID__UINT_UINT_FLOAT_UINT_UINT_UINT,
                              G_TYPE_NONE, 6, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_FLOAT,
		                          G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);

	core_conn_signals[DOWNLOAD_GAPS] =
		g_signal_new ("download-gaps",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, download_gaps),
                              NULL, NULL,
		                          gui_marshal_VOID__POINTER_UINT_UINT_POINTER_POINTER_POINTER,
                              G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT,
                              G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);


	core_conn_signals[SPEED_TOTAL] =
		g_signal_new ("speed-total",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, speed_total),
                              NULL, NULL,
                              gui_marshal_VOID__FLOAT_FLOAT,
                              G_TYPE_NONE, 2, G_TYPE_FLOAT, G_TYPE_FLOAT);

	core_conn_signals[NEW_DOWNLOAD] =
		g_signal_new ("new-download",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, new_download),
                              NULL, NULL,
		                          gui_marshal_VOID__POINTER_STRING_UINT_UINT_STRING_UINT,
                              G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_UINT,
                              G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT);

	core_conn_signals[REMOVE_DOWNLOAD] =
		g_signal_new ("remove-download",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, remove_download),
                              NULL, NULL,
		                          gui_marshal_VOID__POINTER_UINT,
                              G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);

	core_conn_signals[SHARED_FILE_HASHING] =
		g_signal_new ("shared-file-hashing",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, shared_file_hashing),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[SHARED_DIRS] =
		g_signal_new ("shared-dirs",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, shared_dirs),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_POINTER,
                              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);

	core_conn_signals[SHARED_FILES] =
		g_signal_new ("shared-files",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, shared_files),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
                              G_TYPE_NONE, 7, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER,
		                          G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);

	core_conn_signals[STATUS_MESSAGE] =
		g_signal_new ("status-message",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, status_message),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[STATUS_MESSAGE_UNFILTERED] =
		g_signal_new ("status-message-unfiltered",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, status_message_unfiltered),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[ERROR_MESSAGE] =
		g_signal_new ("error-message",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, error_message),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[CLIENT_STATS] =
		g_signal_new ("client-stats",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, client_stats),
                              NULL, NULL,
		                          gui_marshal_VOID__FLOAT_FLOAT_FLOAT_UINT_UINT_UINT,
                              G_TYPE_NONE, 6, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
                              G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);

	core_conn_signals[SERVER_CONNECT_FAILED] =
		g_signal_new ("server-connect-failed",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, server_connect_failed),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[SERVER_CONNECTED] =
		g_signal_new ("server-connected",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, server_connected),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[SERVER_DISCONNECTED] =
		g_signal_new ("server-disconnected",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, server_disconnected),
                              NULL, NULL,
		                          gui_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);

	core_conn_signals[SERVER_LIST] =
		g_signal_new ("server-list",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, server_list),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
                              G_TYPE_NONE, 9, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
	                            G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);

	core_conn_signals[SERVER_STATS] =
		g_signal_new ("server-stats",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, server_stats),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_UINT,
                              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);

	core_conn_signals[UPLOAD_STATUS] =
		g_signal_new ("upload-status",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, upload_status),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_POINTER_POINTER,
                              G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER);

	core_conn_signals[REMOVE_UPLOAD_SLOT] =
		g_signal_new ("remove-upload-slot",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, remove_upload_slot),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT,
                              G_TYPE_NONE, 1, G_TYPE_UINT);

	core_conn_signals[CHANGE_UPLOAD_SLOT] =
		g_signal_new ("change-upload-slot",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, change_upload_slot),
                              NULL, NULL,
		                          gui_marshal_VOID__UINT_STRING_POINTER_STRING_POINTER,
                              G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_POINTER,
		                          G_TYPE_STRING, G_TYPE_POINTER);

	core_conn_signals[SEARCH_EXTENDED] =
		g_signal_new ("search-extended",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, search_extended),
                              NULL, NULL,
		                          gui_marshal_VOID__STRING,
                              G_TYPE_NONE, 1, G_TYPE_STRING);

	core_conn_signals[SEARCH_RESULTS] =
		g_signal_new ("search-results",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (GuiCoreConnClass, search_results),
                              NULL, NULL,
		                          gui_marshal_VOID__BOOLEAN_BOOLEAN_UINT_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
                              G_TYPE_NONE, 16, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_POINTER,
		                          G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
		                          G_TYPE_POINTER,	G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
		                          G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);

	object_class->finalize = gui_core_conn_finalize;
	klass->download_status = NULL;
	klass->speed_total = NULL;
}


/***************************************************************************
 *
 *   gui_core_conn_instance_init
 *
 ***************************************************************************/

static void
gui_core_conn_instance_init (GuiCoreConn *conn)
{
	conn->priv = g_new0 (GuiCoreConnPrivate, 1);

	conn->priv->is_localhost = TRUE;

	conn->priv->is_overnet  = FALSE;
	conn->priv->is_edonkey  = FALSE;
	conn->priv->is_hybrid   = FALSE;
	conn->priv->got_options = FALSE;
	conn->priv->got_data    = FALSE;
	conn->priv->got_stats   = FALSE;

	conn->priv->host       = g_strdup(opt_get_str(OPT_GUI_DEFAULT_CORE_HOST));
	conn->priv->port       = opt_get_int(OPT_GUI_DEFAULT_CORE_PORT);
	conn->priv->username   = g_strdup(opt_get_str(OPT_GUI_DEFAULT_CORE_USER));
	conn->priv->password   = g_strdup(opt_get_str(OPT_GUI_DEFAULT_CORE_PASS));

	conn->priv->packet      = NULL;
	conn->priv->guicoreconn = NULL;
}


/***************************************************************************
 *
 *   gui_core_conn_finalize
 *
 ***************************************************************************/

static void
gui_core_conn_finalize (GObject *object)
{
	GuiCoreConn *conn = (GuiCoreConn *) object;

	g_return_if_fail (GUI_IS_CORE_CONN (conn));

	if (conn->priv->guicoreconn)
		gnet_conn_delete (conn->priv->guicoreconn);

	if (conn->priv->task_timeout > 0)
		g_source_remove(conn->priv->task_timeout);

	G_FREE(conn->priv->coreopts.incomingdir);
	G_FREE(conn->priv->coreopts.tempdir);
	G_FREE(conn->priv->coreopts.nick);
	memset(&(conn->priv->coreopts), 0x00, sizeof(CoreOptions));

	if (conn->priv)
		g_free (conn->priv);

	parent_class->finalize (object);
}


/***************************************************************************
 *
 *   gui_core_conn_get_type
 *
 ***************************************************************************/

GType
gui_core_conn_get_type (void)
{
	static GType type; /* 0 */

	if (type == 0)
	{
		static GTypeInfo info =
		{
			sizeof (GuiCoreConnClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) gui_core_conn_class_init,
			NULL, NULL,
			sizeof (GuiCoreConn),
			0,
			(GInstanceInitFunc) gui_core_conn_instance_init
		};

		type = g_type_register_static (G_TYPE_OBJECT, "GuiCoreConn", &info, 0);
	}

	return type;
}


/***************************************************************************
 *
 *   gui_core_conn_new
 *
 ***************************************************************************/

GuiCoreConn *
gui_core_conn_new (void)
{
	GObject	*obj;

	obj = g_object_new (GUI_TYPE_CORE_CONN, NULL);


	return GUI_CORE_CONN (obj);
}


/***************************************************************************
 *
 *   gui_core_conn_is_alive
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_alive (GuiCoreConn *conn)
{
	g_assert (GUI_IS_CORE_CONN(conn));

	return ((conn->priv->guicoreconn) && conn->priv->conn_status == CONN_STATUS_COMMUNICATING);
}


/***************************************************************************
 *
 *   gui_core_conn_is_overnet
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_overnet (GuiCoreConn *conn)
{
	g_return_val_if_fail ( GUI_IS_CORE_CONN(conn), FALSE );

	return (conn->priv)->is_overnet;
}

/***************************************************************************
 *
 *   gui_core_conn_is_edonkey
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_edonkey (GuiCoreConn *conn)
{
	g_return_val_if_fail ( GUI_IS_CORE_CONN(conn), FALSE );

	return (conn->priv)->is_edonkey;
}

/***************************************************************************
 *
 *   gui_core_conn_is_overnet
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_hybrid (GuiCoreConn *conn)
{
	g_return_val_if_fail ( GUI_IS_CORE_CONN(conn), FALSE );

	return (conn->priv)->is_hybrid;
}

/***************************************************************************
 *
 *   gui_core_conn_is_local
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_local (GuiCoreConn *conn)
{
	return conn->priv->is_localhost;
}


/***************************************************************************
 *
 *   onConnectionStatusClosed
 *
 *   The connection has become dysfunctional
 *    from the core side
 *
 ***************************************************************************/

static void
onConnectionStatusClosed (GConn *conn, GConnEvent *event, GuiCoreConn *coreconn)
{
	if (coreconn->priv->reqstats_timeout)
	{
		g_source_remove(coreconn->priv->reqstats_timeout);
		coreconn->priv->reqstats_timeout = 0;
	}

	gnet_conn_disconnect(coreconn->priv->guicoreconn);

	if (coreconn->priv->conn_status != CONN_STATUS_CLOSED_DELIBERATELY)
	{
		if (event->type == GNET_CONN_TIMEOUT)
		{
			if (coreconn->priv->conn_status == CONN_STATUS_AUTHENTICATING)
				coreconn->priv->conn_status = CONN_STATUS_CLOSED_TIMEOUT_NO_DATA;
			else
				coreconn->priv->conn_status = CONN_STATUS_CLOSED_TIMEOUT;
		}
		else
		{
			coreconn->priv->conn_status = CONN_STATUS_CLOSED_UNEXPECTEDLY;
		}
	}

	g_signal_emit (coreconn, core_conn_signals[CORE_CONN_STATUS], 0, coreconn->priv->conn_status);
}

/***************************************************************************
 *
 *   onConnectionStatusRead
 *
 *   Receive data from the core
 *
 ***************************************************************************/

static void
onConnectionStatusRead (GConn *conn, GConnEvent *event, GuiCoreConn *coreconn)
{
	g_assert ((event) && (event->buffer) && event->length > 0);

	if (coreconn->priv->conn_status != CONN_STATUS_AUTHENTICATING
	    && coreconn->priv->conn_status != CONN_STATUS_COMMUNICATING)
	{
		if (coreconn->priv->packet)
		{
			ramfile_free(coreconn->priv->packet);
			coreconn->priv->packet = NULL;
		}
		return;
	}

	if (!coreconn->priv->got_header)
	{
		const guint32 *fourbytes = (guint32*)(event->buffer + 1);

		g_assert (event->length == 5);         /* we have asked for exactly the header */

		if (*((guint8*)(event->buffer)) != 0xE3)    /* ed2k header byte */
		{
			/* FIXME */
			g_printerr (   "-----\n");
			g_printerr ( _("Invalid packet on GUI-core connection - closing connection.\n") );
			g_printerr (   "-----\n");
			ramfile_dump(coreconn->priv->packet, "invalid packet" );
			gnet_conn_disconnect(coreconn->priv->guicoreconn);
			return;
		}

		coreconn->priv->packetlen  = GUINT32_FROM_LE(*fourbytes);
		coreconn->priv->packet     = ramfile_new(5 + coreconn->priv->packetlen);
		coreconn->priv->got_header = TRUE;

		ramfile_write(coreconn->priv->packet, event->buffer, 5);

//		g_print ("Asking for %u (%p) more bytes (body)\n", coreconn->priv->packetlen, GUINT_TO_POINTER(coreconn->priv->packetlen));

		gnet_conn_readn (coreconn->priv->guicoreconn, coreconn->priv->packetlen);	/* get body of packet */
		gnet_conn_timeout (coreconn->priv->guicoreconn, CONN_TIMEOUT); /* valid data received. Renew timeout. */

		/* Received our first bits of data? */
		if (coreconn->priv->conn_status == CONN_STATUS_AUTHENTICATING)
		{
			g_signal_emit (coreconn, core_conn_signals[CORE_CONN_STATUS], 0, CONN_STATUS_COMMUNICATING);
			coreconn->priv->conn_status = CONN_STATUS_COMMUNICATING;

			if (coreconn->priv->task_timeout == 0)
			{
				coreconn->priv->task_timeout = g_timeout_add(60*1000, (GSourceFunc) onTaskTimeout, coreconn);
				coreconn->priv->minutes = 0;
			}
		}
	}
	else /* now getting the body of the ed2k-packet */
	{
		ramfile_write(coreconn->priv->packet, event->buffer, event->length);

		g_assert (ramfile_get_size(coreconn->priv->packet) == 5 + coreconn->priv->packetlen);

		parse_packet (coreconn);

		ramfile_free(coreconn->priv->packet);
		coreconn->priv->packet = NULL;

		gnet_conn_readn(coreconn->priv->guicoreconn,5);	/* get header of next packet */
		gnet_conn_timeout (coreconn->priv->guicoreconn, CONN_TIMEOUT); /* Valid data received. Renew timeout. */

		coreconn->priv->got_header = FALSE;
		coreconn->priv->packetlen = 0;
	}
}

/***************************************************************************
 *
 *   onTimeoutRequestStats
 *
 *   The core doesn't always seem to actually start sending
 *    the stats if we request them too early for some reason,
 *    so let's send requests until we have any
 *
 ***************************************************************************/

static gboolean
onTimeoutRequestStats (GuiCoreConn *conn)
{
	if (!conn->priv->got_stats)
	{
		gui_core_conn_send_single_command(core,GUI_START_DL_STATUS);
		gui_core_conn_send_single_command(core,GUI_START_UL_STATUS);
		return TRUE; /* call again */
	}

	conn->priv->reqstats_timeout = 0;

	return FALSE; /* do not call again */
}

/***************************************************************************
 *
 *   onConnectionStatusConnected
 *
 *   We have just established the connection to the core
 *
 ***************************************************************************/

static void
onConnectionStatusConnected (GConn *conn, GConnEvent *event, GuiCoreConn *coreconn)
{
	coreconn->priv->conn_status = CONN_STATUS_AUTHENTICATING;

	g_signal_emit (coreconn, core_conn_signals[CORE_CONN_STATUS], 0, CONN_STATUS_AUTHENTICATING);

	if (coreconn->priv->packet)
		ramfile_free (coreconn->priv->packet);

	coreconn->priv->packet     = NULL;
	coreconn->priv->got_header = FALSE;
	coreconn->priv->packetlen  = 0;

	coreconn->priv->got_data    = FALSE;
	coreconn->priv->got_stats   = FALSE;
	coreconn->priv->got_options = FALSE;

	gnet_conn_readn (conn, 5); /* request ed2k packet header */
	gnet_conn_timeout (conn, CONN_TIMEOUT_INITIAL);

	gui_core_conn_send_login(coreconn, coreconn->priv->username, coreconn->priv->password);

	coreconn->priv->closed_deliberately = TRUE; /* avoid reconnect dialog if auto-connect fails */
	coreconn->priv->coreopts.pid = 0;

	/* set core options to some default
	 *  values (will be overridden once
	 *  options are received) */

	coreconn->priv->coreopts.max_dl_speed = 9999.0;
	coreconn->priv->coreopts.max_ul_speed = 9999.0;

	G_FREE(	coreconn->priv->coreopts.incomingdir);
	G_FREE(	coreconn->priv->coreopts.tempdir);
	G_FREE(	coreconn->priv->coreopts.nick);
	coreconn->priv->coreopts.incomingdir = g_strdup("");
	coreconn->priv->coreopts.tempdir     = g_strdup("");
	coreconn->priv->coreopts.nick        = g_strdup(DUMMY_NICKNAME);
	coreconn->priv->coreopts.pid         = 0;

	coreconn->priv->is_hybrid = FALSE;
	coreconn->priv->is_overnet = FALSE;

	/* Send this first thing, so we can find
	 *  out whether it's eDonkey or overnet  */
	gui_core_conn_send_command(coreconn, "g");

	/* do this as first thing, in case the
	 *  DNS lookup takes longer than 15secs */
	gui_core_conn_send_get_options(coreconn);

	coreconn->priv->reqstats_timeout = g_timeout_add (500, (GSourceFunc) onTimeoutRequestStats, coreconn);

	/* if we get a response, we know it's edonkey or hybrid */
	gui_core_conn_send_get_serverlist(coreconn);

	gui_core_conn_send_get_shared_dirs(coreconn);

	if (!opt_get_bool(OPT_GUI_SLOW_GUI_CORE_CONNECTION))
		gui_core_conn_send_get_shared_files(coreconn);
}

/***************************************************************************
 *
 *   onConnectionStatus
 *
 *   Something has happened with the GUI-core-connection
 *
 ***************************************************************************/

static void
onConnectionStatus (GConn *conn, GConnEvent *event, GuiCoreConn *coreconn)
{
	g_assert(event != NULL);

	//g_print ("in %s, event->type = %u\n", __FUNCTION__, event->type);

	switch (event->type)
	{
		case GNET_CONN_CONNECT:
			onConnectionStatusConnected (conn, event, coreconn);
			break;

		case GNET_CONN_READ:
			onConnectionStatusRead (conn, event, coreconn);
			break;

		case GNET_CONN_CLOSE:
		case GNET_CONN_ERROR:
		case GNET_CONN_TIMEOUT:
			onConnectionStatusClosed (conn, event, coreconn);
			break;

		case GNET_CONN_WRITE:
			break;

		case GNET_CONN_WRITABLE:
		case GNET_CONN_READABLE:
			g_return_if_reached();
	}

	return;
}


/***************************************************************************
 *
 *   gui_core_conn_reconnect
 *
 ***************************************************************************/

gboolean
gui_core_conn_reconnect (GuiCoreConn *conn)
{
	g_return_val_if_fail (conn->priv->guicoreconn != NULL, FALSE);
	g_return_val_if_fail (conn->priv->conn_status != CONN_STATUS_CONNECTING,     FALSE);
	g_return_val_if_fail (conn->priv->conn_status != CONN_STATUS_AUTHENTICATING, FALSE);
	g_return_val_if_fail (conn->priv->conn_status != CONN_STATUS_COMMUNICATING,  FALSE);

	conn->priv->is_overnet  = FALSE;
	conn->priv->is_edonkey  = FALSE;
	conn->priv->is_hybrid   = FALSE;
	conn->priv->got_options = FALSE;
	conn->priv->got_data    = FALSE;
	conn->priv->got_stats   = FALSE;

	conn->priv->conn_status = CONN_STATUS_CONNECTING;

	g_signal_emit (conn, core_conn_signals[CORE_CONN_STATUS], 0, CONN_STATUS_CONNECTING);

	gnet_conn_timeout (conn->priv->guicoreconn, CONN_CONNECT_TIMEOUT);
	gnet_conn_connect (conn->priv->guicoreconn);

	return TRUE;
}


/***************************************************************************
 *
 *   gui_core_conn_connect
 *
 ***************************************************************************/

gboolean
gui_core_conn_connect (GuiCoreConn *conn, const gchar *host, guint port, const gchar *user, const gchar *pass)
{
	GInetAddr	*ia;
	gchar		  *ipstr;

	g_return_val_if_fail (host !=NULL && *host != 0x00, FALSE);
	g_return_val_if_fail (user !=NULL && *user != 0x00, FALSE);
	g_return_val_if_fail (pass !=NULL && *pass != 0x00, FALSE);
	g_return_val_if_fail (port > 0, FALSE);

	if (conn->priv->guicoreconn)
	{
		gnet_conn_delete(conn->priv->guicoreconn);
		conn->priv->guicoreconn = NULL;
	}

	ia = gnet_inetaddr_new (host, port);

	if (ia == NULL)
	{
		g_printerr("Failed to get GInetAddr from %s:%u. Hostname not resolvable?\n", host, port);
		g_printerr("(Do you have localhost in /etc/hosts?)\n");
		return FALSE;
	}

	conn->priv->guicoreconn = gnet_conn_new_inetaddr (ia, (GConnFunc) onConnectionStatus, conn);

	g_assert (conn->priv->guicoreconn != NULL);
	g_assert (conn->priv->host != host && conn->priv->username != user && conn->priv->password != pass);

	G_FREE(conn->priv->host);
	G_FREE(conn->priv->username);
	G_FREE(conn->priv->password);

	conn->priv->host     = g_strdup(host);
	conn->priv->port     = port;
	conn->priv->username = g_strdup(user);
	conn->priv->password = g_strdup(pass);

	ipstr = gnet_inetaddr_get_canonical_name(ia);

	conn->priv->is_localhost = (strncmp(ipstr, "127.0.0.", 8) == 0);

	gui_core_conn_reconnect(conn);

	/* the callback will take it from here... */

	gnet_inetaddr_delete (ia);
	g_free(ipstr);

	return TRUE;
}



/***************************************************************************
 *
 *   send_packet
 *
 *  TAKES OWNERSHIP OF PACKET PASSED AND FREES IT!
 *
 ***************************************************************************/

static void
send_packet (GuiCoreConn *conn, ramfile *packet)
{
	guint8   *np;
	guint32   data_size, intel_size;

	g_assert (core != NULL);

	if (!gnet_conn_is_connected(conn->priv->guicoreconn))
	{
		ramfile_free(packet);
		return;
	}

	data_size = ramfile_get_size(packet);
	intel_size = GUINT32_TO_LE(data_size);		// to little endian

	np = g_new (guint8, data_size+5);

	np[0] = 0xE3;
	memcpy (np+1, &intel_size, 4);
	memcpy (np+1+4, ramfile_get_buffer(packet), data_size);

	gnet_conn_write (conn->priv->guicoreconn, np, data_size+5);

//	g_print("sent cmd %3u (size = %u)\n", (guint) np[5], intel_size);

	/* data is copied by gnet_conn_write, so we can delete the buffer here right away */
	g_free(np);

	ramfile_free(packet);
}


/***************************************************************************
 *
 *   gui_core_conn_disconnect
 *
 *   Called to close the connection 'deliberately',
 *    which will also bring up the normal connect-to
 *    dialog (instead of the reconnect dialog)
 *
 ***************************************************************************/

void
gui_core_conn_disconnect (GuiCoreConn *conn)
{
	if (conn->priv->guicoreconn)
		gnet_conn_disconnect(conn->priv->guicoreconn);

	g_signal_emit (conn, core_conn_signals[CORE_CONN_STATUS], 0, CONN_STATUS_CLOSED_DELIBERATELY);
	conn->priv->conn_status = CONN_STATUS_CLOSED_DELIBERATELY;
}


/***************************************************************************
 *
 *   onDeprecatedMessage
 *
 ***************************************************************************/

static gboolean
onDeprecatedMessage (GuiCoreConn *conn, ramfile *packet)
{
	ramfile_seek(packet, 5, RAMFILE_SEEK_FROM_START);
	g_printerr("Received deprecated message #%u from core. Please update your core.\n", (guint) ramfile_read8(packet));
	return TRUE;
}


/***************************************************************************
 *
 *   onCoreOptions
 *
 ***************************************************************************/

static gboolean
onCoreOptions (GuiCoreConn *conn, ramfile *packet)
{
	guint dummy, ver;

	/* we set this to FALSE again here, because we set it
	 * to TRUE when we try to do autoconnect at startup,
	 * so we don't get two donkeys if initial spawn
	 * worked, but not the connect. (FIXME: is this still an issue?) */
	conn->priv->closed_deliberately = FALSE;

	ver = ramfile_read16(packet);

	if (ver == 10019) /* edonkeyclc 1.1.0 */
		ver = 1010;
	else if (ver >= 10000 && ver <= 10300)
		ver = ver/10;

	if ((ver < 1000 || ver > 2000) && conn->priv->coreopts.version != ver)
	{
		g_printerr (_("Core sent unexpected version %u.\n"
		              "Please install a core version 1.0.1 or newer.\n"
		              "See http://ed2k-gtk-gui.sourceforge.net/core.shtml"), ver);
	}
	
	conn->priv->coreopts.version = ver;
	conn->priv->coreopts.baseversion = 0; /* FIXME: still needed? */

	conn->priv->coreopts.max_dl_speed = ramfile_readfloat(packet);
	conn->priv->coreopts.max_ul_speed = ramfile_readfloat(packet);

	conn->priv->coreopts.clientport = ramfile_read16(packet);

	ramfile_seek(packet, 2, RAMFILE_SEEK_FROM_HERE);  /* skip double max connections value */

	G_FREE(conn->priv->coreopts.nick);
	G_FREE(conn->priv->coreopts.tempdir);
	G_FREE(conn->priv->coreopts.incomingdir);

	conn->priv->coreopts.nick        = ramfile_read_i4_string(packet);
	conn->priv->coreopts.tempdir     = ramfile_read_i4_string(packet);
	conn->priv->coreopts.incomingdir = ramfile_read_i4_string(packet);

	conn->priv->coreopts.always_connected = ramfile_read8(packet);  /* core 1.0.1: will give us false (sends wrong flag) */
	dummy                                 = ramfile_read8(packet);  /* autoReconnectUIFlag (not used) */
	conn->priv->coreopts.autoremove_dead  = ramfile_read8(packet);  /* core bug: will give us false   */
	
	if (conn->priv->coreopts.num_gotten_opts == 0 && ver == 1001)
		gui_core_conn_send_command (conn, "auto");
	
	conn->priv->coreopts.allow_pm         = ramfile_read8(packet);
	conn->priv->coreopts.save_corrupted   = ramfile_read8(packet);
	dummy                                 = ramfile_read8(packet);  /* verify cancel - we use our own */
	conn->priv->coreopts.adminport        = ramfile_read16(packet);
	conn->priv->coreopts.maxconns         = ramfile_read32(packet);
	conn->priv->coreopts.builddate        = ramfile_read32(packet); /* core build date                */
	conn->priv->coreopts.line_down_speed  = ramfile_readfloat(packet);
	conn->priv->coreopts.pid              = ramfile_read32(packet);

	g_signal_emit (conn, core_conn_signals[COPTIONS], 0, &conn->priv->coreopts);

	conn->priv->coreopts.num_gotten_opts++;

	return TRUE;
}


/***************************************************************************
 *
 *   onDownloadStatus
 *
 ***************************************************************************/

static gboolean
onDownloadStatus (GuiCoreConn *conn, ramfile *packet)
{
	gboolean newstyle;
	guint16  n, num;
	gfloat   speed;
	guint    id, status, trans, avail, sources;

	conn->priv->current_dl_speed = 0.0;
	core->priv->got_stats = TRUE;

	num = ramfile_read16(packet);

	if (packet_get_data_size(packet) == (3 + (num * 15)))      /* 1 + 2 + (num * (4+1+4+4+1+1)) */
	{
		newstyle = TRUE;
	}
	else
	{
		g_return_val_if_fail (ramfile_get_size(packet) == (8 + (num * 13)), FALSE);
		newstyle = FALSE;
	}

	for (n = 0; n < num; ++n)
	{
		id      = (newstyle) ? (guint) ramfile_read32(packet) : (guint) ramfile_read16(packet);
		status  = (guint) ramfile_read8(packet);
		speed   = ramfile_readfloat(packet);
		trans   = (guint) ramfile_read32(packet);
		avail   = (guint) ramfile_read8(packet);
		sources = (guint) ramfile_read8(packet);

		g_signal_emit (conn, core_conn_signals[DOWNLOAD_STATUS], 0, id, status, speed, trans, avail, sources);

		(conn->priv)->current_dl_speed += speed;
	}

	/* silently ignore values that are too high (we might get these for
	 *  a short time when a corruption has occured */
	if ((conn->priv)->current_dl_speed < 0.0 || (conn->priv)->current_dl_speed > 2048.0)
		(conn->priv)->current_dl_speed = 0.0;

	if ((conn->priv)->current_ul_speed < 0.0 || (conn->priv)->current_ul_speed > 2048.0)
		(conn->priv)->current_ul_speed = 0.0;

	/* We first receive the upload status, and then the download
	 *  status packet, that's why we emit the signal here */
	g_signal_emit (conn, core_conn_signals[SPEED_TOTAL], 0,
	                (conn->priv)->current_dl_speed,
	                (conn->priv)->current_ul_speed);

	return TRUE;
}


/***************************************************************************
 *
 *   onNewDownload
 *
 ***************************************************************************/

static gboolean
onNewDownload (GuiCoreConn *conn, ramfile *packet)
{
	const gchar *name = NULL;
	GPtrArray   *taglist;
	guint32      id, size = 0;
	guint8       hash[16];
	guint        prio;
	gchar       *tmpfn;

	ramfile_read(packet, hash, 16);
	ramfile_seek(packet, 4+2, RAMFILE_SEEK_FROM_HERE);  /* not used any longer */

	taglist = taglist_read_from_ramfile(packet);
	g_return_val_if_fail (taglist != NULL, FALSE);

	taglist_get_string_tag ( taglist, TAGNAME_FILENAME_SHORT, &name );
	taglist_get_int_tag    ( taglist, TAGNAME_FILESIZE_SHORT, &size );

	prio = (guint) ramfile_read8(packet),

	tmpfn = ramfile_read_i4_string(packet); /* temp file name or temp dir */

	if (packet_left_from_pos(packet) >= 4)
	{
		id = ramfile_read32(packet);
	}
	else
	{
		id = G_MAXUINT; /* make list replace this with current number in list */
	}

	g_signal_emit (conn, core_conn_signals[NEW_DOWNLOAD], 0, hash, name, size, prio, tmpfn, id);

//	g_print ("New download with ID %p, hash = %s\n", GUINT_TO_POINTER(id), hash_to_hash_str(hash));

	taglist_free(taglist);

	if (tmpfn)
		g_free(tmpfn);

	return TRUE;
}


/***************************************************************************
 *
 *   onDownloadGaps
 *
 ***************************************************************************/

#define CORE_SENDS_DL_ID_INSTEAD_OF_HASH_WITH_GAPS  \
          (gui_core_conn_is_newer_than(core, 10, 8, 2003))

static gboolean
onDownloadGaps (GuiCoreConn *conn, ramfile *packet)
{
	guint8  hash[16];
	guint   n, num, id = 0;

	if (!CORE_SENDS_DL_ID_INSTEAD_OF_HASH_WITH_GAPS)
		ramfile_read(packet, hash, 16);
	else
		id = ramfile_read32(packet);

	num = ramfile_read16(packet);

	if (1)
	{
		guint startarr[num+1], endarr[num+1], statusarr[num+1];

		for (n = 0; n < num; ++n)
		{
			startarr[n]  = (guint) ramfile_read32(packet);
			endarr[n]    = (guint) ramfile_read32(packet);
			statusarr[n] = (guint) ramfile_read16(packet);
		}

		startarr[num]  = 0;
		endarr[num]    = 0;
		statusarr[num] = 0;

//		g_print ("Got gaps for ID %p\n", GUINT_TO_POINTER(id));

		if ( id > 0 )
			g_signal_emit (conn, core_conn_signals[DOWNLOAD_GAPS], 0, NULL, id, num, startarr, endarr, statusarr);
		else
			g_signal_emit (conn, core_conn_signals[DOWNLOAD_GAPS], 0, hash,  0, num, startarr, endarr, statusarr);
	}

	return TRUE;
}

/***************************************************************************
 *
 *   onRemoveDownload
 *
 ***************************************************************************/

static gboolean
onRemoveDownload (GuiCoreConn *conn, ramfile *packet)
{
	guint8  hash[16], cancelledflag;

	ramfile_read (packet, hash, 16);

	cancelledflag = ramfile_read8 (packet);
	
	if (cancelledflag == 0xff)
		cancelledflag = 0; /* = we don't know */

	g_signal_emit (conn, core_conn_signals[REMOVE_DOWNLOAD], 0, hash, (guint) cancelledflag);

	return TRUE;
}


/***************************************************************************
 *
 *   onHashing
 *
 ***************************************************************************/

static gboolean
onHashing (GuiCoreConn *conn, ramfile *packet)
{
	gchar *fn;

	fn = ramfile_read_i4_string(packet);

	g_signal_emit (conn, core_conn_signals[SHARED_FILE_HASHING], 0, fn);

	g_free(fn);

	return TRUE;
}


/***************************************************************************
 *
 *   onSharedDirs
 *
 ***************************************************************************/

static gboolean
onSharedDirs (GuiCoreConn *conn, ramfile *packet)
{
	guint n, num;

	num = ramfile_read16(packet);

	if (1)
	{
		gchar *dirname_arr[num+1];

		for (n = 0;  n < num;  ++n)
			dirname_arr[n] = ramfile_read_i4_string(packet);

		g_signal_emit (conn, core_conn_signals[SHARED_DIRS], 0, num, dirname_arr);

		for (n = 0;  n < num;  ++n)
			g_free(dirname_arr[n]);
	}

	return TRUE;
}


/***************************************************************************
 *
 *   onSharedFiles
 *
 ***************************************************************************/

static gboolean
onSharedFiles (GuiCoreConn *conn, ramfile *packet)
{
	guint  n, num;

	num = ramfile_read32(packet);

	if (1)
	{
		const guint8 *hash_arr[num+1];
		GPtrArray    *taglist;
		gchar        *format_arr[num+1];
		gchar        *name_arr[num+1];
		gchar        *type_arr[num+1];
		guint         size_arr[num+1];
		guint         prio_arr[num+1];

		memset(prio_arr, 0xff, sizeof(prio_arr));

		for (n = 0;  n < num;  ++n)
		{
			const gchar *name = NULL, *format = NULL, *type = NULL;

			hash_arr[n] = ramfile_get_hash(packet);

			ramfile_seek(packet, 4+2, RAMFILE_SEEK_FROM_HERE); /* unused */

			taglist = taglist_read_from_ramfile (packet);

			g_return_val_if_fail ( taglist != NULL, FALSE );

			taglist_get_int_tag    (taglist, TAGNAME_PRIORITY_SHORT,   prio_arr + n);
			taglist_get_int_tag    (taglist, TAGNAME_FILESIZE_SHORT,   size_arr + n);
			taglist_get_string_tag (taglist, TAGNAME_FILENAME_SHORT,   &name);
			taglist_get_string_tag (taglist, TAGNAME_FILEFORMAT_SHORT, &format);
			taglist_get_string_tag (taglist, TAGNAME_FILETYPE_SHORT,   &type);

			name_arr[n] = g_strdup(name);

			if (format == NULL)
				format_arr[n] = g_strdup(misc_get_extension_from_filename (name));
			else
				format_arr[n] = g_strdup(format);

			if (type == NULL)
				type_arr[n] = g_strdup(misc_get_filetype_from_extension (format));
			else
				type_arr[n] = g_strdup(type);

			taglist_free(taglist); /* we took ownership of the strings */
		}

		g_signal_emit (conn, core_conn_signals[SHARED_FILES], 0, num, hash_arr,
		               name_arr, size_arr, format_arr, type_arr, prio_arr);

		for (n = 0;  n < num;  ++n)
		{
			G_FREE(name_arr[n]);
			G_FREE(type_arr[n]);
			G_FREE(format_arr[n]);
		}
	}

	return TRUE;
}


/***************************************************************************
 *
 *   onUploadStatus
 *
 ***************************************************************************/

static gboolean
onUploadStatus (GuiCoreConn *conn, ramfile *packet)
{
	guint n, num;

	conn->priv->current_ul_speed = 0.0;
	core->priv->got_stats = TRUE;

	num = ramfile_read16(packet);

	g_return_val_if_fail (packet_get_data_size(packet) == (3 + (num * 8)), FALSE); /* 1 + 2 + (num * (4+4)) - new slotID system */

	if (1)
	{
		gfloat speeds[num+1];
		guint  slotids[num+1];

		for (n = 0;  n < num;  ++n)
		{
			slotids[n] = ramfile_read32(packet);
			speeds[n]  = ramfile_readfloat(packet);

			(conn->priv)->current_ul_speed += speeds[n];
		}

		g_signal_emit (conn, core_conn_signals[UPLOAD_STATUS], 0, num, speeds, slotids);
	}

	return TRUE;
}


/***************************************************************************
 *
 *   onRemoveUploadSlot
 *
 ***************************************************************************/

static gboolean
onRemoveUploadSlot (GuiCoreConn *conn, ramfile *packet)
{
	guint32 slotid;

	slotid = ramfile_read32(packet);

	g_signal_emit (conn, core_conn_signals[REMOVE_UPLOAD_SLOT], 0, slotid);

	return TRUE;
}


/***************************************************************************
 *
 *   onChangeUploadSlot
 *
 ***************************************************************************/

static gboolean
onChangeUploadSlot (GuiCoreConn *conn, ramfile *packet)
{
	const gchar  *username = NULL;
	GPtrArray    *usertaglist;
	guint32       slotid;
	guint8        filehash[16], userhash[16];
	gchar        *filename;

	memset(userhash, 0x00, 16); /* we don't need it - just send a zero hash */

	slotid = ramfile_read32(packet);
	filename = ramfile_read_i4_string(packet);

	ramfile_seek(packet, 16 + 4 + 2, RAMFILE_SEEK_FROM_HERE);	/* skip userhash + unused junk */

	usertaglist  = taglist_read_from_ramfile(packet);

	ramfile_read(packet, filehash, 16);

	g_return_val_if_fail ( slotid      != G_MAXUINT, FALSE );
	g_return_val_if_fail ( filename    != NULL,      FALSE );

	if (usertaglist)
		taglist_get_string_tag (usertaglist, TAGNAME_FILENAME_SHORT, &username);

	if (!username)
		username = "";

	g_signal_emit (conn, core_conn_signals[CHANGE_UPLOAD_SLOT], 0, slotid, filename, filehash, username, userhash);

	if (usertaglist)
		taglist_free(usertaglist);

	g_free(filename);

	return TRUE;
}

/***************************************************************************
 *
 *   onStatusMessage
 *
 ***************************************************************************/

static gboolean
onStatusMessage (GuiCoreConn *conn, ramfile *packet)
{
	static const gchar *last_overnet_status; /* NULL */
	gchar              *tmp, *msg, overnetid[64];
	guint               dummy;

	tmp = ramfile_read_i4_string(packet);
	g_return_val_if_fail (tmp != NULL, FALSE);

	for (msg=tmp;((*msg=='\n')||(*msg=='\r')||(*msg==' '));msg++);

	g_signal_emit (conn, core_conn_signals[STATUS_MESSAGE_UNFILTERED], 0, msg);

	if (g_ascii_strncasecmp(msg, "Can't connect to ", 17) == 0)
	{
		g_signal_emit (conn, core_conn_signals[SERVER_CONNECT_FAILED], 0, msg+17);
	}
	else if (g_ascii_strncasecmp(msg, "(open)", 6) == 0)
		last_overnet_status = _(" (open) ");
	else if (g_ascii_strncasecmp(msg, "(firewalled)", 12) == 0)
		last_overnet_status = _(" (firewalled) ");
	else if (g_ascii_strncasecmp(msg, "(unclear)", 9) == 0)
		last_overnet_status = _(" (unclear) ");
	else if (sscanf(msg, "ID: %32[0-9a-fA-F]", overnetid) == 1)
	{

		if (!conn->priv->is_overnet)	/* first time? */
			gui_core_conn_send_get_overnet_contacts(conn);

		if (!conn->priv->is_hybrid) {
		    if (conn->priv->is_edonkey) {
			conn->priv->is_edonkey = FALSE;
			conn->priv->is_hybrid = TRUE;
			g_signal_emit (conn, core_conn_signals[HYBRID_STATUS], 0, msg, (last_overnet_status!=NULL) ? last_overnet_status : "");
		    } else {
			conn->priv->is_overnet = TRUE;
			g_signal_emit (conn, core_conn_signals[OVERNET_STATUS], 0, msg, (last_overnet_status!=NULL) ? last_overnet_status : "");
		    }
		}

	}
	else if (g_ascii_strncasecmp(msg, "Shibboleth: ", 11) == 0)
	{
		conn->priv->is_hybrid  = TRUE;
		conn->priv->is_overnet = FALSE;
		conn->priv->is_edonkey = FALSE;
	}
	else if (g_ascii_strncasecmp(msg, "Auto-connect to servers ", 24) == 0)
	{
		if (g_ascii_strncasecmp(msg+24,"enabled",7) == 0)
			conn->priv->coreopts.always_connected = TRUE;
		else if (g_ascii_strncasecmp(msg+24,"disabled",7) == 0)
			conn->priv->coreopts.always_connected = FALSE;
		else g_print ("[%s] [%s]\n", msg, msg+24);
	}
	else if ( *msg == '('  /* the opening bracket of a peer from 'vp' or of individual DL from 'space' */
	         || strstr(msg, "contacts") != NULL
	         || strstr(msg,"Your ID has changed") != NULL
	         || g_ascii_strncasecmp(msg, "Contact List", 12) == 0
	         || g_ascii_strncasecmp(msg, "Users:", 6) == 0
	         || g_ascii_strncasecmp(msg,"No more servers to extend to", 26) == 0
	         || g_ascii_strncasecmp(msg,"Not connected to a server", 24) == 0
	         || g_ascii_strncasecmp(msg,"Already Connected", 16) == 0
	         || strstr(msg,"IP doesn't match") != NULL                     /* forgotten debug msg in v51.2 */
	         || g_ascii_strncasecmp(msg, "ICLT_YES_PARTNER", 15) == 0      /* forgotten debug msg in v51.2 */
	         || g_ascii_strncasecmp(msg, "ICLT_NO_PARTNER", 13) == 0       /* forgotten debug msg in v51.2 */
	         || g_ascii_strncasecmp(msg, "Not publishing desc", 18) == 0   /* forgotten debug msg in v51.2 */
	         || g_ascii_strncasecmp(msg, "Overnet:", 8) == 0
	         || g_ascii_strncasecmp(msg, "eD2k Disabled", 13) == 0
	         || g_ascii_strncasecmp(msg, "Slot time out:", 14) == 0        /* debug msg in v1.0 */
	         || sscanf(msg,"ClientID: %u (", &dummy) == 1
	         || sscanf(msg,"%u) %u.%u.%u.%u:%u", &dummy, &dummy, &dummy, &dummy, &dummy, &dummy) == 6 /* contact list line */
	         || sscanf(msg,"%u.%u.%u.%u:%u (%u.%u.%u.%u:%u)",
	                   &dummy, &dummy, &dummy, &dummy, &dummy,
	                   &dummy, &dummy, &dummy, &dummy, &dummy) == 10) /* 'g' command output in overnet - own ID */
	{
		; /* do nothing but ignore the message */
	}
	else if (g_ascii_strncasecmp(msg, "Connected to: ", 13) == 0)
	{
		g_signal_emit (conn, core_conn_signals[SERVER_CONNECTED], 0, msg + 14);

		if (!conn->priv->is_hybrid) {
		    if (conn->priv->is_overnet) {
			conn->priv->is_overnet = FALSE;
			conn->priv->is_hybrid = TRUE;
			g_signal_emit (conn, core_conn_signals[HYBRID_STATUS], 0, msg, (last_overnet_status!=NULL) ? last_overnet_status : "");
		    } else
			conn->priv->is_edonkey = TRUE;
		}
	}
	else if (g_ascii_strncasecmp(msg, "eDonkey2000: ", 12) == 0)
	{
		if (!conn->priv->is_hybrid) {
		    if (conn->priv->is_overnet) {
			conn->priv->is_overnet = FALSE;
			conn->priv->is_hybrid = TRUE;
			g_signal_emit (conn, core_conn_signals[HYBRID_STATUS], 0, msg, (last_overnet_status!=NULL) ? last_overnet_status : "");
		    } else
			conn->priv->is_edonkey = TRUE;
		}
	}
	else
	{
		g_signal_emit (conn, core_conn_signals[STATUS_MESSAGE], 0, msg);
	}

	g_free(tmp);
	
	return TRUE;
}

/***************************************************************************
 *
 *   onErrorMessage
 *
 ***************************************************************************/

static gboolean
onErrorMessage (GuiCoreConn *conn, ramfile *packet)
{
	gchar *msg;

	msg = ramfile_read_i4_string(packet);

	g_signal_emit (conn, core_conn_signals[ERROR_MESSAGE], 0, msg);

	g_free(msg);

	return TRUE;
}


/***************************************************************************
 *
 *   onClientStats
 *
 ***************************************************************************/

static gboolean
onClientStats (GuiCoreConn *conn, ramfile *packet)
{
	gfloat     tmp, in, needed;
	guint      on_queue, cons_now, clientid;

	tmp      = ramfile_readfloat(packet);
	in       = ramfile_readfloat(packet);
	needed   = ramfile_readfloat(packet);
	clientid = (guint) ramfile_read32(packet);
	cons_now = (guint) ramfile_read16(packet);
	on_queue = (guint) ramfile_read16(packet);

	g_signal_emit (conn, core_conn_signals[CLIENT_STATS], 0, tmp, in, needed, clientid, cons_now, on_queue);

	if (!gui_core_conn_is_overnet(conn) && !gui_core_conn_is_hybrid(conn))
	{
		const gchar *fwstr;

		if (clientid == 0)
			fwstr = _("not connected");
		else if (clientid > 0xFFFFFF)
			fwstr = _("open");
		else
			fwstr =  _("firewalled");

		g_signal_emit (conn, core_conn_signals[EDONKEY_STATUS], 0, clientid, fwstr);
	}


	return TRUE;
}


/***************************************************************************
 *
 *   onServerConnected
 *
 ***************************************************************************/

static gboolean
onServerConnected (GuiCoreConn *conn, ramfile *packet)
{
	gchar *servername;

	servername = ramfile_read_i4_string(packet);
	g_return_val_if_fail ( servername != NULL, FALSE );

	g_signal_emit (conn, core_conn_signals[SERVER_CONNECTED], 0, servername);

	return TRUE;
}

/***************************************************************************
 *
 *   onServerDisconnected
 *
 ***************************************************************************/

static gboolean
onServerDisconnected (GuiCoreConn *conn, ramfile *packet)
{
	g_signal_emit (conn, core_conn_signals[SERVER_DISCONNECTED], 0);
	return TRUE;
}

/***************************************************************************
 *
 *   onServerList
 *
 ***************************************************************************/

static gboolean
onServerList (GuiCoreConn *conn, ramfile *packet)
{
	guint n, num;

	if (!conn->priv->is_hybrid)
	{
		if (!conn->priv->is_edonkey)	/* first time we get serverlist? */
		{
			/* read in server names to look up, then look them up */
			dns_lookup_read_lookuplist();
			dns_lookup_do_lookup();

			conn->priv->is_edonkey = TRUE;
		}
	}

	num = ramfile_read32(packet);

	g_return_val_if_fail (num < 2000, FALSE);	/* more than 2000 servers is unlikely */

	if (1)
	{
		const gchar *name = NULL, *desc = NULL;
		guint        ip_arr[num+1], port_arr[num+1], ping_arr[num+1];
		guint        pref_arr[num+1], users_arr[num+1], files_arr[num+1];
		gchar       *name_arr[num+1], *desc_arr[num+1];

		for (n = 0;  n < num; ++n)
		{
			GPtrArray    *taglist;

			ramfile_seek(packet, 16, RAMFILE_SEEK_FROM_HERE);	/* not used */

			ip_arr[n]    = ramfile_readIP(packet);
			port_arr[n]  = ramfile_read16(packet);
			name_arr[n]  = NULL;
			desc_arr[n]  = NULL;
			ping_arr[n]  = 0;
			pref_arr[n]  = 0;
			users_arr[n] = 0;
			files_arr[n] = 0;

			taglist = taglist_read_from_ramfile(packet);
			g_return_val_if_fail (taglist != NULL, FALSE);

			name = NULL;
			desc = NULL;

			taglist_get_string_tag (taglist, TAGNAME_FILENAME_SHORT,          &name);
			taglist_get_string_tag (taglist, TAGNAME_SERVERDESCRIPTION_SHORT, &desc);
			taglist_get_int_tag    (taglist, "users",                         &users_arr[n]);
			taglist_get_int_tag    (taglist, "files",                         &files_arr[n]);
			taglist_get_int_tag    (taglist, TAGNAME_PING_SHORT,              &ping_arr[n]);
			taglist_get_int_tag    (taglist, TAGNAME_PREFERENCE_SHORT,        &pref_arr[n]);

			if (name)
				name_arr[n] = g_strdup(name);
			if (desc)
				desc_arr[n] = g_strdup(desc);

			taglist_free(taglist);
		}

		g_signal_emit (conn, core_conn_signals[SERVER_LIST], 0, num, ip_arr, port_arr,
		               name_arr, desc_arr, users_arr, files_arr, ping_arr, pref_arr);
	}

	return TRUE;
}

/***************************************************************************
 *
 *   onServerStats
 *
 ***************************************************************************/

static gboolean
onServerStats (GuiCoreConn *conn, ramfile *packet)
{
	guint32 files, users;

	files = ramfile_read32(packet);
	users = ramfile_read32(packet);

	g_signal_emit (conn, core_conn_signals[SERVER_STATS], 0, files, users);

	return TRUE;
}


/***************************************************************************
 *
 *   onExtendingSearch
 *
 ***************************************************************************/

static gboolean
onExtendingSearch (GuiCoreConn *conn, ramfile *packet)
{
	gchar *servername;

	servername = ramfile_read_i4_string(packet);
	if (servername)
	{
		g_signal_emit (conn, core_conn_signals[SEARCH_EXTENDED], 0, servername);
		g_free(servername);
		return TRUE;
	}

	return FALSE;
}


/***************************************************************************
 *
 *   read_seach_results
 *
 ***************************************************************************/

static gboolean
read_seach_results (GuiCoreConn *conn, ramfile *packet, guint num, gboolean overnetmode, gboolean from_main_server)
{
	const gchar  *format_arr[num+1];
	const gchar  *type_arr[num+1];
	const gchar  *artist_arr[num+1];
	const gchar  *title_arr[num+1];
	const gchar  *album_arr[num+1];
	const gchar  *length_arr[num+1];
	const gchar  *codec_arr[num+1];
	const gchar  *name_arr[num+1];           /*  FIXME:       = "(no name)"; */
	GPtrArray	   *taglist_arr[num+1];
	gboolean      more = FALSE;
	guint8		   *servicehash_arr[num+1];
	guint8		   *hash_arr[num+1];
	guint         size_arr[num+1];
	guint         avail_arr[num+1];
	guint         bitrate_arr[num+1];
	guint         i;

	memset(format_arr,  0x00, sizeof(format_arr));
	memset(type_arr,    0x00, sizeof(type_arr));
	memset(artist_arr,  0x00, sizeof(artist_arr));
	memset(title_arr,   0x00, sizeof(title_arr));
	memset(album_arr,   0x00, sizeof(album_arr));
	memset(length_arr,  0x00, sizeof(length_arr));
	memset(codec_arr,   0x00, sizeof(codec_arr));
	memset(name_arr,    0x00, sizeof(name_arr));
	memset(size_arr,    0x00, sizeof(size_arr));
	memset(avail_arr,   0x00, sizeof(avail_arr));
	memset(bitrate_arr, 0x00, sizeof(bitrate_arr));

	for (i = 0;  i < num;  ++i)
	{
		if (overnetmode)
		{
			servicehash_arr[i] = ramfile_read(packet, NULL, 16);
			hash_arr[i]        = ramfile_read(packet, NULL, 16);
		}
		else
		{
			hash_arr[i] = ramfile_read(packet, NULL, 16);
			ramfile_seek(packet, 4+2, RAMFILE_SEEK_FROM_HERE); /* skip 4+2 unused bytes */
			servicehash_arr[i] = NULL;
		}

		taglist_arr[i] = taglist_read_from_ramfile (packet);

		g_return_val_if_fail ( taglist_arr[i] != NULL, FALSE );

		if (taglist_get_string_tag (taglist_arr[i], TAGNAME_FILENAME_SHORT, name_arr + i) == TRUE )
		{
			/* remove bad characters (like a slash) from the filename */
			(void) g_strdelimit((gchar*)name_arr[i], "/\\&", ' ');
		}

		(void) taglist_get_int_tag (taglist_arr[i], TAGNAME_FILESIZE_SHORT, size_arr + i );
		(void) taglist_get_int_tag (taglist_arr[i], TAGNAME_AVAILABILITY_SHORT, avail_arr + i );
		(void) taglist_get_int_tag (taglist_arr[i], "bitrate", bitrate_arr + i );

		(void) taglist_get_string_tag (taglist_arr[i], "Artist", artist_arr + i );
		(void) taglist_get_string_tag (taglist_arr[i], "Title",  title_arr  + i );
		(void) taglist_get_string_tag (taglist_arr[i], "Album",  album_arr  + i );
		(void) taglist_get_string_tag (taglist_arr[i], "length", length_arr + i );
		(void) taglist_get_string_tag (taglist_arr[i], "codec",  codec_arr  + i );

		(void) taglist_get_string_tag (taglist_arr[i], TAGNAME_FILETYPE_SHORT,   type_arr   + i );
		(void) taglist_get_string_tag (taglist_arr[i], TAGNAME_FILEFORMAT_SHORT, format_arr + i );
	}

	if (from_main_server)
		more = (ramfile_read8(packet) > 0);

	g_signal_emit (conn, core_conn_signals[SEARCH_RESULTS], 0, from_main_server, more, num,
	               hash_arr, servicehash_arr, size_arr, avail_arr, name_arr, type_arr,
	               format_arr, artist_arr, album_arr, title_arr, length_arr,
	               bitrate_arr, codec_arr);

	for (i = 0;  i < num;  ++i)
	{
		if (taglist_arr[i])
			taglist_free(taglist_arr[i]);

		G_FREE(hash_arr[i]);
		G_FREE(servicehash_arr[i]);
	}

	return TRUE;
}


/***************************************************************************
 *
 *   onSearchResult
 *
 *   Receive an 'extended' search result or an overnet result
 *
 ***************************************************************************/

static gboolean
onSearchResult (GuiCoreConn *conn, ramfile *packet)
{
	ramfile_seek(packet, 5, RAMFILE_SEEK_FROM_START);

	if (ramfile_read8(packet) == CORE_ED_SEARCH_RESULT) /* cdonkey? */
		return read_seach_results(conn, packet, 1, FALSE, FALSE);

	return read_seach_results(conn, packet, 1, gui_core_conn_is_overnet(conn), FALSE);
}


/***************************************************************************
 *
 *   onSearchResults
 *
 *   Receive a list of results from the main edonkey server
 *
 ***************************************************************************/

static gboolean
onSearchResults (GuiCoreConn *conn, ramfile *packet)
{
	guint cmd, num;

	ramfile_seek(packet, 5, RAMFILE_SEEK_FROM_START);

	cmd = ramfile_read8(packet);

	num = ramfile_read32(packet);

	if (cmd == CORE_ED_NEW_SEARCH_RESULT) /* cdonkey? */
		return read_seach_results(conn, packet, num, FALSE, TRUE);

	return read_seach_results(conn, packet, num, gui_core_conn_is_overnet(conn), TRUE);
}


/***************************************************************************
 *
 *   onTaskTimeout
 *
 *   Does all kinds of things we want to do in regular intervals
 *    (refresh shared dirs list, etc.)
 *
 ***************************************************************************/

static gboolean
onTaskTimeout (GuiCoreConn *conn)
{
	if (conn->priv->conn_status != CONN_STATUS_COMMUNICATING)
	{
		g_source_remove(conn->priv->task_timeout);
		conn->priv->task_timeout = 0;
		conn->priv->minutes = 0;
		return FALSE;
	}

	++conn->priv->minutes;

	gui_core_conn_send_get_shared_dirs(conn);
	gui_core_conn_send_get_stats(conn);

	if (conn->priv->is_overnet || conn->priv->is_hybrid)
	{
		gui_core_conn_send_get_overnet_contacts(conn);
		gui_core_conn_send_command(conn, "g");	/* get current ID and firewalled status */
	}

	/* get shared files every 30 mins if not a slow connection */
	if ((conn->priv->minutes % 30) == 0  &&  !opt_get_bool(OPT_GUI_SLOW_GUI_CORE_CONNECTION))
		gui_core_conn_send_get_shared_files(core);

	/* get shared files every 90 mins if it is a slow connection */
	if ((conn->priv->minutes % 90) == 0  &&  opt_get_bool(OPT_GUI_SLOW_GUI_CORE_CONNECTION))
		gui_core_conn_send_get_shared_files(core);

	return TRUE; /* call again */
}

/***************************************************************************
 *
 *   parse_packet
 *
 ***************************************************************************/

static void
parse_packet (GuiCoreConn *conn)
{
	guint8   cmd, c;

	g_assert (GUI_IS_CORE_CONN(conn));
	g_assert (conn->priv->packet != NULL);

	ramfile_seek(conn->priv->packet, 5, RAMFILE_SEEK_FROM_START); /* skip header */

	cmd = ramfile_read8(conn->priv->packet);

	for (c = 0;  msg_handlers[c].cmd != 0;  ++c)
	{
		if (msg_handlers[c].cmd != cmd)
			continue;

		++msg_handlers[c].count;
		msg_handlers[c].payload += packet_get_data_size(conn->priv->packet);

		if (msg_handlers[c].handler == NULL)
			continue;

//		g_print("got %s\n", msg_handlers[c].cmdstr);

		if (!msg_handlers[c].handler(conn, conn->priv->packet))
		{
			g_warning("Message handler '%s' could not parse message from core.\n", msg_handlers[c].cmdstr);
			ramfile_dump(conn->priv->packet, "packet");
		}
	}

	/* TODO: check how performance difference this makes for
	 *       the usual case where no handlers are connected */
	if (cmd != CORE_STATUS_MSG)
		g_signal_emit (conn, core_conn_signals[STATUS_MESSAGE_UNFILTERED], 0, NULL);
}


/***************************************************************************
 *
 *   gui_core_conn_dump_message_stats
 *
 ***************************************************************************/

void
gui_core_conn_dump_message_stats (void)
{
	guint c, numtotal = 0, sizetotal = 0;

	g_print("----------------------------------------\n");
	g_print ("%25s  %6s  %6s\n", "message", "num", "size");
	g_print("----------------------------------------\n");

	for (c = 0;  msg_handlers[c].cmd != 0;  ++c)
	{
		g_print ("%25s  %6u %6uk\n", msg_handlers[c].cmdstr, msg_handlers[c].count, (msg_handlers[c].payload >> 10));
		numtotal  += msg_handlers[c].count;
		sizetotal += msg_handlers[c].payload;
	}

	g_print ("\n%25s  %6u %6uk\n\n", "Total number of messages", numtotal, (sizetotal >> 10) );
}

#if 0
/***************************************************************************
 *
 *   gui_core_conn_get_base_version
 *
 ***************************************************************************/

guint
gui_core_conn_get_base_version (GuiCoreConn *conn)
{
	return conn->priv->coreopts.baseversion;
}
#endif

/***************************************************************************
 *
 *   gui_core_conn_is_newer_than
 *
 ***************************************************************************/

gboolean
gui_core_conn_is_newer_than (GuiCoreConn *conn, guint day, guint month, guint year)
{
	guint checkdate;

	g_return_val_if_fail (day >= 1 && day <= 31 && month >=1 && month <=12 && year >= 2002 && year <= 2004, FALSE);

	checkdate = (day | (month << 8) | ((year-2000) << 16));

	return (checkdate <= conn->priv->coreopts.builddate);
}


/***************************************************************************
 *
 *   gui_core_conn_get_core_options
 *
 ***************************************************************************/

const CoreOptions *
gui_core_conn_get_core_options (GuiCoreConn *conn)
{
	return (const CoreOptions*) &(conn->priv->coreopts);
}


/***************************************************************************
 *
 *   gui_core_conn_get_host
 *
 ***************************************************************************/

const gchar *
gui_core_conn_get_host (GuiCoreConn *conn, guint *port)
{
	if (port)
		*port = conn->priv->port;

	return (const gchar*) conn->priv->host;
}


/* --------------------------------------------------------------------------------- */


/***************************************************************************
 *
 *   atExit
 *
 ***************************************************************************/

static void
atExit (void)
{
	GTimer *timer;

	if (gui_core_conn_is_alive(core))
	{
		gui_core_conn_send_single_command(core, GUI_STOP_DL_STATUS);
		gui_core_conn_send_single_command(core, GUI_STOP_UL_STATUS);

		if (opt_get_bool(OPT_GUI_SHUTDOWN_CORE_ON_EXIT))
		{
			dns_lookup_remove_dns_servers ();
			gui_core_conn_send_logout(core);
		}

		/* We do this so core tries to send sth after
		 *  we closed the socket and recognises that
		 *  we're gone ('Another control attempt'
		 *  problem on second connect to core) */

		gui_core_conn_send_get_shared_files(core);

		/* Try to make sure our stuff gets sent out
		 *  (but wait max. 1 second) */
		timer = g_timer_new();

		while (g_main_context_pending(NULL)  &&  g_timer_elapsed(timer, NULL) < 1.0)
			g_main_context_iteration(NULL, FALSE);

		g_timer_destroy(timer);
	}

	g_object_unref(G_OBJECT(core));
	core = NULL;
}

/***************************************************************************
 *
 *   gui_core_connection_init
 *
 *   create the global GuiCoreConn object
 *
 ***************************************************************************/

void
gui_core_connection_init (void)
{
	core = gui_core_conn_new();

	g_atexit(atExit);

	return;
}


/* --------------------------------------------------------------------------------- */

/***************************************************************************
 *
 *   gui_core_conn_send_command_with_data
 *
 ***************************************************************************/

void
gui_core_conn_send_command_with_data (GuiCoreConn *conn, guint8 cmd, const guint8 *buf, gsize buflen)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (buf != NULL || buflen == 0);
	g_return_if_fail (buf == NULL || buflen > 0);

	packet = ramfile_new(1 + buflen);

	ramfile_write8 (packet, cmd);

	if (buflen > 0)
		ramfile_write(packet, buf, (gint) buflen);

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_login
 *
 ***************************************************************************/

void
gui_core_conn_send_login (GuiCoreConn *conn, const gchar *user, const gchar *pass)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (user!=NULL);
	g_return_if_fail (pass!=NULL);

	packet = ramfile_new(1+2+1+2+1); /* packet will grow bigger automatically */

	ramfile_write8 (packet, CONT_LOGIN);
	ramfile_write_i4_string (packet, user);
	ramfile_write_i4_string (packet, pass);

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_logout
 *
 ***************************************************************************/

void
gui_core_conn_send_logout (GuiCoreConn *conn)
{
	/* Set timeout to a very short interval, in
	 *  order to avoid freeze problem (gnet doesn't
	 *  seem to catch that the remote side closed
	 *  the connection in all cases and somehow
	 *  prevents GUI/gtk redraws until it breaks out
	 *  with an error after a loong time
	 * Happens when gnet receives a READ before the
	 *  connection breaks off apparently. */
	gnet_conn_timeout (conn->priv->guicoreconn, 1500); /* = 1.5 secs */

	gui_core_conn_send_single_command(conn, CONT_STOP);

	conn->priv->conn_status = CONN_STATUS_CLOSED_DELIBERATELY;

	/* signal should be emitted when the connection breaks off */
}


/***************************************************************************
 *
 *   gui_core_conn_send_server_command
 *
 ***************************************************************************/

void
gui_core_conn_send_server_command (GuiCoreConn *conn, guint8 cmd, guint32 ip, guint port)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));

	packet = ramfile_new(1 + 4 + 2);

	ramfile_write8  (packet, cmd);
	ramfile_writeIP (packet, ip);
	ramfile_write16 (packet, port);

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_server_pref
 *
 ***************************************************************************/

void
gui_core_conn_send_set_server_pref (GuiCoreConn *conn, guint32 ip, guint port, guint8 newpref)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));

	packet = ramfile_new(1 + 4 + 2 + 1);

	ramfile_write8  (packet, GUI_SETPRI_SERVER);
	ramfile_writeIP (packet, ip);
	ramfile_write16 (packet, port);
	ramfile_write8  (packet, newpref);

	send_packet (conn, packet);
}





#if 0

/***************************************************************************
 *
 *   gui_core_conn_send_type_search
 *
 *   Search for files of a certain filetype
 *
 ***************************************************************************/

void
gui_core_conn_send_type_search (GuiCoreConn *conn, const gchar *str, const gchar *type, guint32 i_minbitrate)
{
	ramfile		*packet;
	gboolean	 audiosearch;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (!gui_core_conn_is_overnet(conn) || gui_core_conn_is_hybrid (conn));

	packet = ramfile_new (1+ 3+2+0+ 4+2+0);

	audiosearch = ((type) && (*type) && g_ascii_strcasecmp(type,"audio")==0);

	ramfile_write8  (packet, GUI_SEARCH);

	if (audiosearch)
	{
		ramfile_write8  (packet, 0x00);         /* compound */
		ramfile_write8  (packet, 0x00);         /* AND      */
	}

	if ((type) && (*type))
	{
		ramfile_write8  (packet, 0x00);         /* compound  */
		ramfile_write8  (packet, 0x00);         /* AND       */

		ramfile_write8  (packet, 0x02);         /* left node */
		ramfile_write_i4_string (packet, type);
		ramfile_write_i4_string (packet, TAGNAME_FILETYPE_SHORT);
	}

	if ((str) && (*str))
	{
		ramfile_write8  (packet, 0x01);         /* right node */
		ramfile_write_i4_string (packet, str);
	} else ramfile_write8 (packet, 0x04);     /* match any  */

	if (audiosearch)
	{
		ramfile_write8  (packet, 0x03);              /* match int        */
		ramfile_write32 (packet, i_minbitrate);      /* minimum bitrate  */
		ramfile_write8  (packet, 0x03);              /* greater or equal */
		ramfile_write_i4_string (packet, "bitrate"); /* in bitrate tag   */
	}

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_type_search_with_extra_options
 *
 *   Search for string of a certain filetype with extra options
 *
 ***************************************************************************/

void
gui_core_conn_send_type_search_with_extra_options (GuiCoreConn  *conn,
                                                   const gchar  *str,
                                                   const gchar  *type,
                                                   guint32       i_minsize,
                                                   guint32       i_maxsize,
                                                   guint32       i_minavail,
                                                   guint32       i_minbitrate)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (!gui_core_conn_is_overnet(conn) || gui_core_conn_is_hybrid (conn));

	packet = ramfile_new (2+2+2+9+9+2+1+9);

	ramfile_write8  (packet, GUI_SEARCH);

	/* FIXME: implement more refined overnet search support (needs core support) */

	/* first node -> compound node (2 bytes) */
	ramfile_write8  (packet, 0x00);		/* compound search node */
	ramfile_write8  (packet, 0x00);		/* AND operation        */

		/* left node second level (2 bytes) */
		ramfile_write8  (packet, 0x00);		/* compound search node */
		ramfile_write8  (packet, 0x00);		/* AND operation        */

			/* left node third level (2 bytes) */
			ramfile_write8  (packet, 0x00);		/* compound search node */
			ramfile_write8  (packet, 0x00);		/* AND operation        */

				/* left node 4th level (9 bytes) */
				ramfile_write8  (packet, 0x03);								/* match int     */
				ramfile_write32 (packet, i_maxsize);					/* maximum size  */
				ramfile_write8  (packet, 0x04);								/* less or equal */
				ramfile_write_i4_string (packet, TAGNAME_FILESIZE_SHORT);	/* in filesize tag */

				/* right node 4th  level (9 bytes) */
				ramfile_write8  (packet, 0x03);								/* match int */
				ramfile_write32 (packet, i_minsize);					/* minimum size */
				ramfile_write8  (packet, 0x03);								/*  greater or equal */
				ramfile_write_i4_string (packet, TAGNAME_FILESIZE_SHORT);	/* in filesize tag */

			/* right node third level (2 bytes) */
			if ((type) && (g_ascii_strncasecmp(type,"any",3)!=0))
			{
				ramfile_write8 (packet, 0x02);					/* find string in tag */
				ramfile_write_i4_string (packet, type);
				ramfile_write_i4_string (packet, TAGNAME_FILETYPE_SHORT);
			}
			else
			{
				ramfile_write8 (packet, 0x04);	/* match any if no type specified */
			}

		/* right node second level (2 bytes) */
		ramfile_write8  (packet, 0x00);		/* compound search node */
		ramfile_write8  (packet, 0x00);		/* AND operation */

			/* left node third level (>=1 bytes) */
			if ((str) && (*str))
			{
				ramfile_write8  (packet, 0x01);					/* simple string search */
				ramfile_write_i4_string (packet, str);
			}
			else
			{
				ramfile_write8  (packet, 0x04);		/* match all if no string given */
			}

			/* right node third level (2 bytes) */
			ramfile_write8  (packet, 0x00);		/* compound search node */
			ramfile_write8  (packet, 0x00);		/* AND operation        */

				/* left node 4th level (9 bytes) */
				ramfile_write8 (packet, 0x03);									/* match int */
				ramfile_write32 (packet, i_minavail);						/* min avail */
				ramfile_write8 (packet, 0x03);									/* greater or equal */
				ramfile_write_i4_string (packet, TAGNAME_AVAILABILITY_SHORT);	/* in availability tag */

				/* right node 4th level (only match bitrate if we have type 'audio') */
				if ((type) && (g_ascii_strncasecmp(type,"audio",5)==0))
				{
					ramfile_write8  (packet, 0x03);               /* match int */
					ramfile_write32 (packet, i_minbitrate);       /* minimum bitrate */
					ramfile_write8  (packet, 0x03);               /* greater or equal */
					ramfile_write_i4_string (packet, "bitrate");  /* in bitrate tag */
				}
				else
				{
					ramfile_write8 (packet, 0x04);	              /* match any if no type specified */
				}

	send_packet (conn, packet);
}

/***************************************************************************
 *
 *   gui_core_conn_send_search_with_extra_options
 *
 *    Search for simple string with extra options
 *
 ***************************************************************************/

#define ramfile_write_int_search_node(rf,val,compmode,tagname) \
          ramfile_write8(rf,0x03);   /* match integer node */ \
          ramfile_write32(rf,val);\
          ramfile_write8(rf,compmode);\
          ramfile_write_i4_string(rf,tagname);

#define ramfile_write_compound_AND_node(rf) \
          ramfile_write8(rf, 0x00);		/* compound search node */ \
          ramfile_write8(rf, 0x00);		/* AND operation */

void
gui_core_conn_send_search_with_extra_options (GuiCoreConn  *conn,
                                              const gchar  *str,
                                              guint32       i_minsize,
                                              guint32       i_maxsize,
                                              guint32       i_minavail)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (!gui_core_conn_is_overnet(conn) || gui_core_conn_is_hybrid (conn));

	packet = ramfile_new (2+2+9+9+2+1+9);

	ramfile_write8  (packet, GUI_SEARCH);

	ramfile_write_compound_AND_node(packet);

		ramfile_write_compound_AND_node(packet);
			ramfile_write_int_search_node(packet, i_maxsize, 0x04, TAGNAME_FILESIZE_SHORT); /* 0x04 = less or equal */
			ramfile_write_int_search_node(packet, i_minsize, 0x03, TAGNAME_FILESIZE_SHORT); /* 0x03 = greater or equal */

		ramfile_write_compound_AND_node(packet);
			ramfile_write_int_search_node(packet, i_minavail, 0x03, TAGNAME_AVAILABILITY_SHORT);
			if ((str) && (*str))
			{
				ramfile_write8  (packet, 0x01);					/* simple string match */
				ramfile_write_i4_string (packet, str);
			}
			else
			{
				ramfile_write8  (packet, 0x04);				/* match any if no string given */
			}

	send_packet (conn, packet);
}
#endif

/***************************************************************************
 *
 *   gui_core_conn_send_simple_search
 *
 *   Search for a string
 *
 ***************************************************************************/

void
gui_core_conn_send_simple_search (GuiCoreConn *conn, const gchar *str)
{
	ramfile	*packet;

	/* FIXME: implement more refined overnet search support (needs core support) */
	if (gui_core_conn_is_overnet(core) || gui_core_conn_is_hybrid (core))
	{
		gchar *s = g_strdup_printf ("s %s", str);
		gui_core_conn_send_command(conn, s);
		g_free(s);
		return;
	}

	packet = ramfile_new (1+1+2+0);

	ramfile_write8  (packet, GUI_SEARCH);
	ramfile_write8  (packet, 0x01);  /* 0x01 = FIND_STR search node type */
	ramfile_write_i4_string (packet, str);

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_new_download
 *
 *   Make a new download from a random ed2k-link
 *
 ***************************************************************************/

void
gui_core_conn_send_new_download (GuiCoreConn *conn, const guint8 *hash, guint32 size, const gchar *fn)
{
	ramfile	*packet;

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (hash!=NULL);
	g_return_if_fail (fn!=NULL);

	packet = ramfile_new (1+  16+4+2+  4+  1+2+1+4+  1+2+1+2+0);

	ramfile_write8 (packet, GUI_NEW_DOWNLOAD);

	/* Now, we send a FileMeta record, in principle...
	 *	(a) hash + ip + port */
	ramfile_write (packet, hash, 16);
	ramfile_writeIP (packet, 0);	/* unused */
	ramfile_write16 (packet, 0);	/* unused */
	/*	(b) metatags */
	ramfile_write32 (packet, 2);	/* 2 tags */

	/*	Tag 1 - filesize */
	ramfile_write8  (packet, 0x03);	/* Tag type 3 = integer tag                          */
	ramfile_write16 (packet, 1);    /* Tag name len = 1 => special tag (abbreviation)    */
	ramfile_write8  (packet, 0x02);	/* Tag name = 0x02 => filesize                       */
	ramfile_write32 (packet, size);	/* filesize integer value                            */

	/*	Tag 2 - filename */
	ramfile_write8  (packet, 0x02); /* Tag type 2 = string tag                           */
	ramfile_write16 (packet, 1);    /* Tag name len = 1 => special tag (abbreviation)    */
	ramfile_write8  (packet, 0x01); /* Tag name = 0x01 => file name                      */
	ramfile_write_i4_string (packet, fn);	/* file name string                            */

	send_packet (conn, packet);
}


/***************************************************************************
 *
 *   gui_core_conn_send_resume_download
 *
 ***************************************************************************/

void
gui_core_conn_send_resume_download (GuiCoreConn *conn, const guint8 *hash)
{
	gui_core_conn_send_command_with_data(conn, GUI_RESUME_DOWNLOAD, hash, 16);
 	gui_core_conn_send_get_download_gaps(conn, hash);
}


/***************************************************************************
 *
 *   gui_core_conn_send_pause_download
 *
 ***************************************************************************/

void
gui_core_conn_send_pause_download (GuiCoreConn *conn, const guint8 *hash)
{
	gui_core_conn_send_command_with_data(conn, GUI_PAUSE_DOWNLOAD, hash, 16);
 	gui_core_conn_send_get_download_gaps(conn, hash);
}


/***************************************************************************
 *
 *   gui_core_conn_send_unshare_dir_no
 *
 ***************************************************************************/

void
gui_core_conn_send_unshare_dir_no (GuiCoreConn *conn, guint dir_no)
{
	gchar	*buf = g_strdup_printf ("u %u", dir_no);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_add_shared_dir
 *
 ***************************************************************************/

void
gui_core_conn_send_add_shared_dir (GuiCoreConn *conn, const gchar *dir)
{
	gchar *buf = g_strconcat("a ", dir, NULL);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_add_shared_dir_structure
 *
 ***************************************************************************/

void
gui_core_conn_send_add_shared_dir_structure (GuiCoreConn *conn, const gchar *dir)
{
	gchar *buf = g_strconcat("a+ ", dir, NULL);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_nickname
 *
 ***************************************************************************/

void
gui_core_conn_send_set_nickname (GuiCoreConn *conn, const gchar *nick)
{
	gchar	*buf;
	g_return_if_fail (nick!=NULL);
	buf = g_strdup_printf ("name %s", nick);
	g_return_if_fail (buf!=NULL);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_incoming
 *
 ***************************************************************************/

void
gui_core_conn_send_set_incoming (GuiCoreConn *conn, const gchar *dir)
{
	gchar	*buf;
	g_return_if_fail (dir!=NULL);
	buf = g_strdup_printf ("in %s", dir);
	g_return_if_fail (buf!=NULL);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_temp
 *
 ***************************************************************************/

void
gui_core_conn_send_set_temp (GuiCoreConn *conn, const gchar *dir)
{
	gchar	*buf;
	g_return_if_fail (dir!=NULL);
	buf = g_strdup_printf ("temp %s", dir);
	g_return_if_fail (buf!=NULL);
	gui_core_conn_send_command(conn, buf);
	g_free (buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_command
 *
 ***************************************************************************/

void
gui_core_conn_send_command (GuiCoreConn *conn, const gchar *cmdstr)
{
	ramfile	*packet = ramfile_new(1+2+0);
	g_return_if_fail (cmdstr!=NULL);
	ramfile_write8 (packet, CONT_CMD);
	ramfile_write_i4_string (packet, cmdstr);
	send_packet (core, packet);
}

/***************************************************************************
 *
 *   gui_core_conn_send_set_port
 *
 ***************************************************************************/

void
gui_core_conn_send_set_port (GuiCoreConn *conn, guint16 port)
{
	gchar	buf[32];
	g_snprintf (buf, sizeof(buf), "port %u", port);
	gui_core_conn_send_command(conn, buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_admin_port
 *
 ***************************************************************************/

void
gui_core_conn_send_set_admin_port (GuiCoreConn *conn, guint16 aport)
{
	gchar	buf[32];
	g_snprintf (buf, sizeof(buf), "aport %u", aport);
	gui_core_conn_send_command(conn, buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_max_speeds
 *
 ***************************************************************************/

void
gui_core_conn_send_set_max_speeds (GuiCoreConn *conn, gfloat dlmax, gfloat ulmax)
{
	gchar buf[64];
	g_snprintf (buf, sizeof(buf), "dumax %.1f %.1f", dlmax, ulmax);
	gui_core_conn_send_command(conn, buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_max_connections
 *
 ***************************************************************************/

void
gui_core_conn_send_set_max_connections (GuiCoreConn *conn, guint maxcon)
{
	gchar	buf[32];
	g_snprintf (buf, sizeof(buf), "mcon %u", maxcon);
	gui_core_conn_send_command(conn, buf);
}


/***************************************************************************
 *
 *   gui_core_conn_send_auto_connect
 *
 ***************************************************************************/

void
gui_core_conn_send_auto_connect (GuiCoreConn *conn, gboolean autoconnectON)
{
	if (autoconnectON)
		gui_core_conn_send_command (conn, "auto +");
	else
		gui_core_conn_send_command (conn, "auto -");

	gui_core_conn_send_get_options (conn);
}


/***************************************************************************
 *
 *   gui_core_conn_send_save_corrupted
 *
 ***************************************************************************/

void
gui_core_conn_send_save_corrupted (GuiCoreConn *conn, gboolean savecorruptedON)
{
	if (savecorruptedON)
		gui_core_conn_send_command(conn, "save");
	else
		gui_core_conn_send_command(conn, "save -");
}


/***************************************************************************
 *
 *   gui_core_conn_send_auto_server_remove
 *
 ***************************************************************************/

void
gui_core_conn_send_auto_server_remove (GuiCoreConn *conn, gboolean autoserverremoveON)
{
	if (autoserverremoveON)
		gui_core_conn_send_command(conn, "asr");
	else
		gui_core_conn_send_command(conn, "asr -");
}


/***************************************************************************
 *
 *   gui_core_conn_send_set_download_priority
 *
 ***************************************************************************/

void
gui_core_conn_send_set_download_priority (GuiCoreConn *conn, const guint8 *hash, guint8 priority)
{
	guint8  buf[16+1];

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (hash!=NULL);

	memcpy(buf, hash, 16);
	buf[16] = priority;

	gui_core_conn_send_command_with_data(conn, GUI_SETPRI_DOWNLOAD, buf, 17);
}


/******************************************************************************
 *
 *   gui_core_conn_send_set_upload_priority
 *
 ******************************************************************************/

void
gui_core_conn_send_set_upload_priority (GuiCoreConn *conn, const guint8 *hash, guint newprio)
{
	guint8  buf[16+1];

	g_return_if_fail (GUI_IS_CORE_CONN(conn));
	g_return_if_fail (hash!=NULL);
	g_return_if_fail (newprio >= 0 && newprio <= 2);

	memcpy(buf, hash, 16);
	buf[16] = (guint8) newprio;

	gui_core_conn_send_command_with_data(conn, GUI_SET_UPLOAD_PRIORITY, buf, 17);
}


/***************************************************************************
 *
 *   gui_core_conn_send_get_stats
 *
 *   will give us among other things peer stats, space, client stats
 *
 ***************************************************************************/

void
gui_core_conn_send_get_stats (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn, GUI_GET_CLIENT_STATS);
}


/******************************************************************************
 *
 *   gui_core_conn_send_get_overnet_contacts
 *
 ******************************************************************************/

void
gui_core_conn_send_get_overnet_contacts (GuiCoreConn *conn)
{
	static time_t  lastasked, now;       /* 0 */

	g_return_if_fail (gui_core_conn_is_edonkey(conn) == FALSE);

	now = time(NULL);

	if ( lastasked > 0 && (now-lastasked) >= 45 )
		return;

	gui_core_conn_send_command(conn, "vc");

	lastasked = now;
}

void
gui_core_conn_send_get_serverlist (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_GET_SERVERLIST);
}

void
gui_core_conn_send_connect_to_server (GuiCoreConn *conn, guint32 ip, guint16 port)
{
	gui_core_conn_send_server_command(conn,GUI_CONNECT,ip,port);
}

void
gui_core_conn_send_connect_to_any_server (GuiCoreConn *conn)
{
	gui_core_conn_send_server_command(conn,GUI_CONNECT,0,0);
}

void
gui_core_conn_send_disconnect_from_server (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_DISCONNECT);
}

void
gui_core_conn_send_add_server (GuiCoreConn *conn, guint32 ip, guint16 port)
{
	gui_core_conn_send_server_command(conn,GUI_ADD_SERVER,ip,port);
}

void
gui_core_conn_send_remove_server (GuiCoreConn *conn, guint32 ip, guint16 port)
{
	gui_core_conn_send_server_command(conn,GUI_DELETE_SERVER,ip,port);
}


void
gui_core_conn_send_extended_search (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_EXSEARCH);
}

void
gui_core_conn_send_more_search (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_MORESEARCH);
}

void
gui_core_conn_send_get_shared_dirs (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_GET_SHARE_DIRS);
}

void
gui_core_conn_send_get_shared_files (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_GET_SHARE_FILES);
}

void
gui_core_conn_send_get_options (GuiCoreConn *conn)
{
	gui_core_conn_send_single_command(conn,GUI_GET_OPTIONS);
	
	/* core 1.0.1 will not send this properly (also see onCoreOptions) */
	if (conn->priv->coreopts.version == 1001)
 		gui_core_conn_send_command (conn, "auto");
}

void
gui_core_conn_send_get_download_gaps (GuiCoreConn *conn, const guint8 *hash)
{
	gui_core_conn_send_command_with_data(conn,GUI_GET_DOWNLOAD_GAP_DETAILS,hash,16);
}


void
gui_core_conn_send_cancel_download (GuiCoreConn *conn, const guint8 *hash)
{
	gui_core_conn_send_command_with_data(conn,GUI_CANCEL_DOWNLOAD,hash,16);
}


