/*
    Silky - A GTK+ client for SILC.
    Copyright (C) 2003, 2004, 2005 Toni Willberg

    - command functions, utilities

    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 program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    http://silky.sourceforge.net/

*/

#include <glade/glade.h>
#include <gtk/gtk.h>
#include <silcincludes.h>
#include <silcclient.h>

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "ui_console.h"
#include "ui_query.h"

#include "support.h"
#include "commands.h"
#include "preferences.h"
#include "log.h"
#include "callbacks.h"
#include "buddylist.h"
#include "gui.h"
#include "xmllayout.h"
#include "ui_channel.h" /* get_active_channel_name() FIXME, shouldn't be used */

#include "common.h"

extern silkyStruct *silky;

extern SilkyXMLElement xml_elements[];

extern GladeXML *xmlmain;
extern gchar *previous_nick; /* my nickname just before sending NICK command */
extern SilkyChannel *channels;


/*
   cmd_msg callback thingy
   This is called after user does /MSG nickname
*/
void on_new_msg_resolved_cmd_msg(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void *context) {
  gint value;
  gchar *message;
  SilcClientID *clientid;
  gchar *str_clientid;
  SilcMessageFlags sendflags;
  SilcClientEntry cliententry;

  debug("found '%d' clients", clients_count);

  if (clients_count == 0) {
    debug("\tDid not find anyone");
    errordialog(_("No users found."));
    return;
  }

  if (clients_count > 1) {
    printconsole(_("There are many people with same nickname, message not sent. Please specify the target."));

    /* FIXME: make sure this isn't illegal way */
    on_show_new_query_window(NULL, NULL);

    return;
    /*
      for (index=0; index < clients_count; index++) {
      debug("\tFound %s@%s (%s@%s) '%s' (%s)",
      clients[index]->nickname,
      clients[index]->server,
      clients[index]->username,
      clients[index]->hostname,
      clients[index]->realname,
      silc_fingerprint(clients[index]->fingerprint,
      clients[index]->fingerprint_len )
      );
      }
    */
    // FIXME: ask user which user to send the message to!
  }

  clientid = clients[0]->id;

  str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT);

  cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid);
  if (!cliententry) {
    debug("Did not get cliententry on_send..");
    printconsole(_("No such user."));
    return;
  }

  message = strdup(context);
  if (strlen(message) <= 0) {
    debug("empty message, skipped");
    return;
  }

  sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
  value = prefs_str_to_bool(prefs_get("sign_private_messages"));

  /* whetter to SIGN message or not */
  if (value) {
    sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
  }

  silc_client_send_private_message(silky->client, silky->conn, cliententry,
				   sendflags, message,
				   strlen(message), FALSE); /* force send TRUE */

  debug_info("private message sent to %s.", cliententry->nickname);
  printquery(clientid, silky->conn->nickname, sendflags, message);


}



/* --------------------------------------------------------- */
/* COMMAND HANDLING STUFF STARTS HERE */
static int
cmd_help (char *tbuf, char *word[], char *word_eol[])
{
  int i = 0, longfmt = 0;
  gchar *helpcmd = "", *buf = malloc(4096);
  debug("cmd_help()");
  if (tbuf)
    helpcmd = word[2];
    if (*helpcmd && strcmp (helpcmd, "-l") == 0)
      longfmt = 1;

      if (*helpcmd && !longfmt)
      {
	command_help (tbuf, helpcmd, FALSE);
      } else
      {
        /*struct popup *pop;*/
        /* GSList *list = command_list; for user commands */
        int t = 1, j;
        snprintf(buf, 4096, "\n%s:\n\n  ", _("Commands Available"));
        if (longfmt)
        {
          while (1)
          {
            if (!xc_cmds[i].name)
              break;
            if (!xc_cmds[i].help || *xc_cmds[i].help == '\0')
              g_snprintf (buf + strlen(buf), 4096, "   %s :\n", xc_cmds[i].name);
            else
              g_snprintf (buf + strlen(buf), 4096, "   %s : %s\n", xc_cmds[i].name, _(xc_cmds[i].help));
            printconsole (buf);
            i++;
          }
          buf[0] = 0;
        } else
        {
	while (1)
        {
          if (!xc_cmds[i].name)
            break;
          strcat (buf, xc_cmds[i].name);
          t++;
          if (t == 6)
          {
            t = 1;
            strcat (buf, "\n  ");
          } else
          for (j = 0; j < (10 - strlen (xc_cmds[i].name)); j++)
            strcat (buf, " ");
          i++;
        }
        }
        g_snprintf( buf + strlen(buf), 4096, "\n\n%s\n\n",
                    _("Type /HELP <command> for more information, or /HELP -l") );
        g_snprintf( buf + strlen(buf), 4096, "%s:\n\n  ",  _("User defined commands") );
        t = 1;
		/* while (list)
		{
			pop = (struct popup *) list->data;
			strcat (buf, pop->name);
			t++;
			if (t == 6)
			{
				t = 1;
				strcat (buf, "\n");
				printconsole (buf);
				strcpy (buf, "  ");
			} else
			{
				if (strlen (pop->name) < 10)
				{
					for (j = 0; j < (10 - strlen (pop->name)); j++)
						strcat (buf, " ");
				}
			}
			list = list->next;
		}*/
        strcat (buf, "\n");
        printconsole (buf);
        free (buf);
        printconsole( g_strdup_printf("\n%s:\n", _("Plugin defined commands")) );


      }
return TRUE;
}

/* There must be a better way to do this? perhaps change on_ui_send to support actions */
static int
cmd_me (char *tbuf, char *word[], char *word_eol[])
{
  gchar *channel_name;
  SilcChannelEntry channel;
  GtkEntry *inmes;
  gchar *message;
  SilcMessageFlags sendflags;
  GtkNotebook *storednotebook;
  GtkWidget *active_page;
  SilcClientID *clientid;
  gchar *str_clientid;
  SilcClientEntry cliententry;
  gint value; /* from config */

  debug_info("cmd_me()");

  /* Get value of the input box */
    inmes = GTK_ENTRY(glade_xml_get_widget (xmlmain, "inputbox"));

  /* Show an error dialog if there's no connection */
  if (!CONNECTED) {
    debug("Can not send a message.\nYou are not connected to a server.");
    printconsole(_("You are not connected to server."));
    return 0;
  }


  /* get active channel name from UI (support.c) */
  channel_name = get_active_channel_name();

  debug("\treturned from get_active_channel_name");

  /* was it channel message or query message */
  if (channel_name) {
    debug("sending channel message");

    /* get channel by name */
    channel = silc_client_get_channel(silky->client, silky->conn, channel_name);

    if (!channel) {
      debug("can not find channel entry!");
      printconsole(_("Can not send message at this time."));
      return 0;
    }
    else {
      debug("got channel");
    }



    /* get a copy of the message, next function wants other type */
    message = strdup(word_eol[2]);

    if (!message || strlen(message) <= 0) {
      debug("empty message, skipping!");
      return FALSE;
    }

    sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
    sendflags |= SILC_MESSAGE_FLAG_ACTION;
    value = prefs_str_to_bool(prefs_get("sign_channel_actions"));

    /* whetter to SIGN message or not */
    if (value) {
      sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
    }

    silc_client_send_channel_message(silky->client, silky->conn, channel,
				     NULL, sendflags,
				     message, strlen(message), FALSE);

    print_mime_to_channel( silky->conn->local_entry, channel->channel_name, 0, g_strdup("text/plain"), (int)sendflags, message);

    /* Clear the textbox */
    gtk_entry_set_text(GTK_ENTRY(inmes), "");
    free(message);

  }
  else {
    debug("Was not channel message.");

  }

  storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
  active_page = gtk_notebook_get_nth_page(storednotebook, gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook) ) );

  clientid = g_object_get_data(G_OBJECT(active_page), "clientid");


  if (clientid) {
    debug("sending query/private message");

    str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT);

    cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid);
    if (!cliententry) {
      debug("Did not get cliententry on_send..");
      printconsole(_("No such user."));
      return 0;
    }

    debug("target: '%s'", cliententry->nickname);

    /* get a copy of the message, next function wants other type */
    /* so we don't send the /me part */
    message = strdup(word_eol[2]);

    if (strlen(message) <= 0) {
      return FALSE;
    }
    sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
    sendflags |= SILC_MESSAGE_FLAG_ACTION;
    value = prefs_str_to_bool(prefs_get("sign_private_actions"));

    /* wether to SIGN message or not */
    if (value) {
      sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
    }


    silc_client_send_private_message(silky->client, silky->conn, cliententry,
				     sendflags, message,
				     strlen(message), FALSE); /* force send TRUE */

    debug("private message sent.");
    printquery(clientid, silky->conn->nickname, sendflags, message);

    /* Clear the textbox */
    gtk_entry_set_text(GTK_ENTRY(inmes), "");
  }
  else {
    debug("Was not private message");
  }
  return 2;
}

static int
cmd_nick (char *tbuf, char *word[], char *word_eol[])
{

  const gchar *new_nick;
  const gchar *old_nick;

  debug_info("cmd_nick()");

  if (!CONNECTED) {
    printconsole(_("You are not connected to server."));
    return FALSE;
  }

  new_nick = strdup(word[2]);
  old_nick = silky->conn->nickname;

  if (strlen(new_nick) <1 ) {
    printconsole(_("Nickname can not be empty."));
    return FALSE;
  }

  if (new_nick && old_nick && !strcmp(new_nick, old_nick) ) {
    printconsole(_("That is your current nickname."));
    return FALSE;
  }

  /* set global variable for later use */
  previous_nick = strdup(old_nick);
  silc_client_command_call(silky->client, silky->conn, NULL, "NICK", new_nick, NULL);
  debug("\tCommand sent. New entry says the nick is '%s'", silky->conn->nickname);

  return 2;
}

/* sends a raw <COMMAND> [parameters] to the client library */
static int
cmd_raw (char *tbuf, char *word[], char *word_eol[])
{
  gchar *cmd = strdup(word_eol[2]);
  debug_info("cmd_raw(%s)", cmd );

  if (silc_client_command_call(silky->client, silky->conn, cmd)) {
    debug("cmd was success");
    return 2;
  }
  else {
    debug("cmd failed");
  }

  return 2;
}


static int
cmd_part (char *tbuf, char *word[], char *word_eol[])
{
  gchar *channel_name;
  /* SilcBuffer idp;*/
  debug_info("cmd_part()");

  /* get the channel name */
  if (*word[2] && *word[2] != '*' ) { /* name given and it's not * */
    channel_name = word[2];
  }
  else {
    /* part current channel */
    channel_name = get_active_channel_name();
  }
  /* require some channel name */
  if (!channel_name  || strlen(channel_name) < 1) {
    debug("No channel name given, can not part.");
    return FALSE;
  }

  if (!CONNECTED){
    return FALSE;
  }

  if (channel_name) {
    debug("leaving channel '%s'", channel_name);
    printconsole(g_strdup_printf(_("Leaving channel '%s'."), channel_name));
    silc_client_command_call(silky->client, silky->conn, NULL, "LEAVE", channel_name, NULL);
  }
  else {
    debug("was not able to find channel name, leave aborted");
  }

  return 2;

}

static int
cmd_join (char *tbuf, char *word[], char *word_eol[])
{
  const gchar *channel_name;
  debug_info("cmd_join()");

  /* get the channel name */
  channel_name = word[2];
  /* require some channel name */
  if (!strlen(channel_name)) {
    debug("No channel name given, can not join.");
    return FALSE;
  }

  if (!CONNECTED){
    debug("No connection. FIXME, should offer reconnection?");
    return 0;
  }

  silky_join_channel(channel_name);

  return 2;
}

/*
   Used when quitting from Silky.
 */
static int
cmd_quit (char *tbuf, char *word[], char *word_eol[])
{
  gchar quitmsg[129];
  gint size = sizeof(quitmsg);

  *quitmsg = '\0';

  debug_info("cmd_quit");

  if (strlen(word[2])) {        /* no need to populate quitmsg if !firstarg */
    debug("quitting with reason '%s'", word[2]);
    if (strlen(word_eol[2]) > 128) {
      size = sizeof(quitmsg); /* let's set correct length so we can nul-terminate it */
    } else {
      size = strlen(word_eol[2]);
    }
    strncpy(quitmsg, word_eol[2], size);
    quitmsg[size] = '\0';
  } else {
    debug("quit without parameter, using default\n");
    strncpy(quitmsg, prefs_get("quit_message"), size);
  }

  if (CONNECTED) {
    debug("setting state to STATE_DISCONNECTING");
    silky->state = STATE_DISCONNECTING;
    printconsole(_("Disconnecting..."));
    silc_client_command_call(silky->client, silky->conn, NULL, "QUIT", quitmsg, NULL);
    /* the callback, silky_finish_disconnect, will set the state */
  }
  else {
    debug("not connected, no need to disconnect\n");
  }

  printconsole(_("Quitting. Thank you for using Silky!"));
  while (gtk_events_pending()) {
    gtk_main_iteration_do(FALSE); /* tell GTK to print previous stuff before we enter blocking command next... */
  }

  /* tell GTK we are leaving */
  gtk_main_quit();
  return 2; /* we should never reach here */
}

/*
  Clears the textview of active tab
*/
static int
cmd_clear ()
{
  GtkTextView *textview = silky_get_active_textview();
  GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(textview);
  
  debug_info("clearning textview");
  if (textbuffer) {
    gtk_text_buffer_set_text(textbuffer, "", 0); /* clear the view */
    debug("done");
    return 1;
  } else {
    debug("textbuffer not found");
    return 0;
  }

}


/*
 Disconnects from connected server
*/
static int
cmd_disconnect (char *tbuf, char *word[], char *word_eol[])
{
  gchar quitmsg[129];
  gint size = sizeof(quitmsg);

  if (DISCONNECTED) {
    debug_info ("STATE_DISCONNECTED, aborting");
    printconsole(_("You are not connected to server."));
    return FALSE;
  }
  if (DISCONNECTING) {
    debug_info("currently disconnecting, STATE_DISCONNECTING");
    printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
    return FALSE;
  }
  if ( CONNECTING ) {
    debug_info("connection already in progress, STATE_CONNECTING");
    printconsole(_("You are currently connecting to a server, please wait."));
    return FALSE;
  }


  *quitmsg = '\0';

  debug_info("on cmd_disconnect");

  if (strlen(word[2])) {        /* no need to populate quitmsg if !firstarg */
    if (strlen(word_eol[2]) > 128) {
      size = sizeof(quitmsg); /* let's set correct length so we can nul-terminate it */
    } else {
      size = strlen(word_eol[2]);
    }
    strncpy(quitmsg, word_eol[2], size);
    quitmsg[size] = '\0';
  } else {
    strncpy(quitmsg, prefs_get("quit_message"), size);
  }

  debug("setting state to STATE_DISCONNECTING");
  silky->state = STATE_DISCONNECTING;
  printconsole(_("Disconnecting..."));
  silc_client_command_call(silky->client, silky->conn, NULL, "QUIT", quitmsg, NULL);

  debug("command sent");

  return 2;
}

/*
 Connects to a server
*/
static int
cmd_server (char *tbuf, char *word[], char *word_eol[])
{
  gchar *new_server;
  int ret;
  char *port;
  char *pass;
  GtkWidget *menuwidget;

  debug_info("cmd_server");

  /* connection ok */
  if ( CONNECTED ) {
    debug_info("already connected, STATE_CONNECTED");
    printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
    return FALSE;
  }

  /* just connecting */
  if ( CONNECTING ) {
    debug_info("connection already in progress, STATE_CONNECTING");
    printconsole(_("You are already connecting to a server, please wait."));
    return FALSE;
  }

  if ( DISCONNECTING ) {
    debug_info("currently disconnecting, STATE_DISCONNECTING");
    printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
    return FALSE;
  }

  /*window = glade_xml_get_widget (xmlmain, "window_connect_server");
  input = GTK_ENTRY(glade_xml_get_widget (xmlmain, "new_servername"));
  new_server = strdup(gtk_entry_get_text(input));*/
  new_server = strdup(word[2]);
  port = strdup(word[3]);
  /* not used yet? */
  pass = strdup(word[4]);

  if (strlen(new_server) < 1) {
    printconsole(_("Invalid server address."));
    return FALSE;
  }

  debug("Setting state to STATE_CONNECTING");
  silky->state = STATE_CONNECTING;

  /* now let's disable Connect and Disconnect for a while, other will be
   enabled later */
  menuwidget = glade_xml_get_widget (xmlmain, "menu_server_disconnect");
  gtk_widget_set_sensitive(menuwidget, FALSE);

  menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect");
  gtk_widget_set_sensitive(menuwidget, FALSE);


  /* close all tabs, but console tab, on reconnection */
  close_all_tabs();

  printconsole(g_strdup_printf(_("Connecting to server '%s'..."), new_server));
  if (*port) {
    ret = silc_client_connect_to_server(silky->client, NULL, atoi(port),
				      new_server, silky);
  }
  else { /* default port */
    ret = silc_client_connect_to_server(silky->client, NULL, 706,
				      new_server, silky);
  }
  if (ret == -1) {
    debug_info("Can not connect to the server. (why?), setting STATE_DISCONNECTED");
    silky->state = STATE_DISCONNECTED;
    printconsole(_("Can not connect to the server.")); /* FIXME: why can't ?? */
    menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect");
    gtk_widget_set_sensitive(menuwidget, TRUE);
    return 0;
  }
  else {
    debug_info("\tConnected to %s.", new_server);
    /* state and menu will be set in the callback function */

  }

  return 2;
}


static int
cmd_msg(char *tbuf, char *word[], char *word_eol[]) {
  gchar *msgnick;
  gchar *msg;
  debug_info("cmd_msg()");

  msgnick = strdup(word[2]);

  if (!msgnick) {
    debug("Did not get anything from input.");
    return FALSE;
  }

  msg = strdup(word_eol[3]);
  if (!msg) {
    debug("Did not get anything from input.");
    return FALSE;
  }

  debug("setting callback... library will call on_new_query_resolved_cmd_msg() after this");

  silc_client_get_clients_whois(silky->client, silky->conn, msgnick, NULL, NULL, on_new_msg_resolved_cmd_msg, msg);


  return 2;
}

/* /set command */
static int
cmd_set(char *tbuf, char *word[], char *word_eol[]) {
  gchar *attr = word[2], *attr_val = word_eol[3];
  gchar *val = NULL;

  debug_info("attr = '%s', attr_val = '%s'", attr, attr_val);
  if ( !g_utf8_strlen(attr, -1) ) {
    /* just list all attributes and abort */
    prefs_show_to_console();
    return 2;
  }

  if( !(val = prefs_get(attr)) ) {
    printconsole(g_strdup_printf(_("Unknown option '%s'."), attr));
    return 2;
  }

  /* we now know that attr is a valid pref. option */
  if( !g_utf8_strlen(attr_val, -1) ) {
    printconsole(g_strdup_printf("%s = %s", attr, val));
    return 2;
  }

  switch(prefs_widget_type(attr)) {
    case WIDGET_CHECKBUTTON:
    /* sanity check, checkbuttons only eat boolean values */
      if( g_ascii_strcasecmp(attr_val, "true") && g_ascii_strcasecmp(attr_val, "false") &&
          g_ascii_strcasecmp(attr_val, "yes") && g_ascii_strcasecmp(attr_val, "no") &&
          g_ascii_strcasecmp(attr_val, "on") && g_ascii_strcasecmp(attr_val, "off") &&
	  g_ascii_strcasecmp(attr_val, "1") && g_ascii_strcasecmp(attr_val, "0") &&
	  g_ascii_strcasecmp(attr_val, "toggle") ) {
	printconsole(g_strdup_printf("Valid options are 'ON', 'OFF' and 'TOGGLE' for this variable."));
	debug("invalid value for boolean variable, not setting '%s'", attr);
	return 2;
      }
    /* adjust attr_val if user wants to toggle */
      if( !g_ascii_strcasecmp(attr_val, "toggle") ) {
        debug("toggling value ('%s') of '%s'", val, attr);
        attr_val = prefs_bool_to_str( !prefs_str_to_bool(val) );
      }
  }

  if( !prefs_set(attr, attr_val) ) {
    debug("Could not set '%s' to '%s'", attr, attr_val);
    /* FIXME: i18n this after 0.5.1 */
    printconsole(g_strdup_printf("Could not set '%s' to '%s'", attr, attr_val));
    return 2;
  }

  printconsole(g_strdup_printf(_("Set %s to %s"), attr, attr_val));
  return 2;

} /* End of cmd_set() */


/*
  LIST
*/

static int
cmd_list(char *tbuf, char *word[], char *word_eol[]) {
  gint rc;

  debug_info("cmd_list()");
  silc_client_command_call(silky->client, silky->conn, NULL, "LIST", NULL);

  rc = TRUE;

  return(rc);

} /* End of cmd_list() */


/*
  WATCH
*/
static int
cmd_watch(char *tbuf, char *word[], char *word_eol[]) {
  SilcBuffer idp;

  debug_info("cmd_watch(%s %s)", word[2], word[3]);

  if ( ( !strcmp(word[2], "") || !strcmp(word[3], "") ) ) {
    debug("empty or missing parameters");
    return FALSE;
  }

  idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT);

  if  (!strcmp(word[2], "-add")) {
    watch_add(word[3]);
    debug("added");
    return TRUE;
  }

  if  (!strcmp(word[2], "-del")) {
    watch_del(word[3]);
    debug("removed");
    return TRUE;
  }

  debug("incorrect second parameter");
  return FALSE;
}


/*
  GETKEY
*/
static int
cmd_getkey(char *tbuf, char *word[], char *word_eol[]) {
	int			 rc;

	debug_info("cmd_getkey(%s)", word[2]);
	if ( !strcmp(word[2], "") ) { /* fixme: safe? */
	  rc = FALSE;
	}
	else {
	  silc_client_command_call(silky->client, silky->conn, NULL, "GETKEY", word[2], NULL);
	  rc = TRUE;
	}
	return(rc);
}


/*
  WHOIS nickname OR WHOIS -pubkey keyfile
*/
static int
cmd_whois(char *tbuf, char *word[], char *word_eol[]) {
	int rc;

	debug_info("cmd_whois(%s %s)", word[2], word[3]);
	if ( !strcmp(word[2], "") ) { /* fixme: safe? */
	  /* empty */
	  rc = FALSE;
	} else if ( !strcmp(word[2], "-pubkey") && strcmp(word[3], "") ) {
	  debug("doing whois by public key file");
	  /* -pubkey filename */
	  silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", word[2], word[3], NULL);
	  rc = TRUE;
	}
	else {
	  /* nickname */
	  debug("doing whois by nickname");
	  silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", word[2], NULL);
	  rc = TRUE;
	}



	return(rc);

} /* End of cmd_whois() */




/* *MUST* be kept perfectly sorted for the bsearch to work */

const struct commands xc_cmds[] = {

  /* format: "COMMAND", function, connection needed (0|1), need to be on a channel (0|1), help text */

	/****************** Not Implemented. ********************

		{"ALIAS",
			cmd_alias, 0, 0, N_("ALIAS [<name> [<body>]]")},
		{"AWAY",
			cmd_away, 1, 0, N_("AWAY [<reason>], sets you away")},
		{"BAN", cmd_ban, 1, 1,
			 N_("BAN <mask> [<bantype>], bans everyone matching the"
			 "mask from the current channel."
			 "If they are already on the channel this doesn't kick "
			 "them (needs chanop)")},

	**** End of non-implemented commands ***************/

		{"CLEAR",
			cmd_clear, 0, 0, N_("CLEAR, "
			"Clears the current text window")},

	/************** Not Implemented. ********************

		{"CLOSE",
			cmd_close, 0, 0, N_("CLOSE, "
			"Closes the current window/tab")},
		{"CYCLE",
			cmd_cycle, 1, 1, N_("CYCLE, "
			"parts current channel and immediately rejoins")},
		{"DEOP", cmd_deop, 1, 1, N_("DEOP <nick>, "
			"removes chanop status from the nick on the current "
			"channel (needs chanop)")},
	**** End of non-implemented commands ***************/

		{"DISCONNECT", cmd_disconnect, 1, 0, N_("DISCONNECT, "
			"Disconnects from server")},

		{"GETKEY", cmd_getkey, 1, 0, N_("GETKEY <user> Requests for public key of the user.")},

		{"HELP", cmd_help, 0, 0, 0},


	/**************** Not Implemented. ********************
		{"IGNORE", cmd_ignore, 0, 0,
	 		N_("IGNORE <mask> <types..> <options..>\n"
	 		"    mask - host mask to ignore, eg: *!*@*.aol.com\n"
	 		"    types - types of data to ignore, one or all of:\n"
	 		"            PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
	 		"    options - NOSAVE, QUIET")},

	**** End of non-implemented commands *****************/

		{"JOIN", cmd_join, 1, 0, N_("JOIN <channel>, "
			"joins the channel")},

	/**************** Not Implemented. ********************

		{"KICK", cmd_kick, 1, 1, N_("KICK <nick>, "
			"kicks the nick from the current channel (needs chanop)")},
		{"KICKBAN", cmd_kickban, 1, 1, N_("KICKBAN <nick>, "
			"bans then kicks the nick from the current channel "
			"(needs chanop)")},

	**** End of non-implemented commands *****************/

		{"LIST", cmd_list, 1, 0,
			N_("LIST, Lists the channels on the server.")},

		{"ME", cmd_me, 1, 0, N_("ME <action>, "
			"sends the action to the current channel "
			"(actions are written in 3rd person, like "
			"/me jumps)")},


		{"MSG", cmd_msg, 1, 0, N_("MSG <nickname> <message>, "
			"sends a private message to nickname")},

		{"NICK", cmd_nick, 0, 0, N_("NICK <nickname>, "
			"sets your nick")},

	/*************** Not Implemented. ********************

		{"NOTIFY", cmd_notify, 0, 0, N_("NOTIFY [<nick>], "
			"lists your notify list or adds someone to it")},
		{"OP", cmd_op, 1, 1, N_("OP <nick>, "
			"gives chanop status to the nick (needs chanop)")},

	**** End of non-implemented commands ****************/

		{"PART", cmd_part, 1, 1, N_("PART [<channel>], "
			"leaves the channel, by default the current one")},
		// {"QUERY", cmd_query, 1, 0, N_("QUERY <nickname>, "
		{"QUERY", cmd_msg, 1, 0, N_("QUERY <nickname>, "
			"opens up a new private conversation window with someone")},
		{"QUIT", cmd_quit, 0, 0, N_("QUIT [<reason>], "
			"disconnects from the current server, if any and quits Silky")},

		/* temporary command, should be commented out from release version! */
		{"RAW", cmd_raw, 0, 0, N_("RAW <command> [arguments], "
					  "sends given SILC command to the server")},

	/**************** Not Implemented. ********************

		{"RECONNECT", cmd_reconnect, 0, 0, N_("RECONNECT "
			"[<host>] [<port>] [<password>], Can be called just as "
			"/RECONNECT to reconnect to the current server or "
			"with /RECONNECT ALL to reconnect to all the "
			"open servers")}
		{"SAY", cmd_say, 0, 0, N_("SAY <text>, "
			"sends the text to the object in the current window")},
		{"SERVCHAN", cmd_servchan, 0, 0, N_("SERVCHAN <host> <port> "
			"<channel>, connects and joins a channel")},

	**** End of non-implemented commands *****************/

		{"SERVER", cmd_server, 0, 0, N_("SERVER <host> [<port>] "
			"[<password>], connects to a server, "
			"the default port is 706")},
 		{"SET", cmd_set, 0, 0, N_("SET [<variable> [<value>]] Gets or sets a config variable") },

	/**************** Not Implemented. ********************

		{"TOPIC", cmd_topic, 1, 1, N_("TOPIC [<topic>], "
			"sets the topic if one is given, "
			"else shows the current topic")},
		{"UNBAN", cmd_unban, 1, 1, N_("UNBAN <mask> "
			"[<mask>...], unbans the specified masks.")},
		{"UNIGNORE", cmd_unignore, 0, 0, N_("UNIGNORE <mask> "
			"[QUIET]")},

	**** End of non-implemented commands *****************/

		{"WATCH", cmd_watch, 1, 0, N_("WATCH -add <nickname>|-del <nickname> Add or remove user to watch list.") },
		{"WHOIS", cmd_whois, 1, 0, N_("WHOIS <nickname> Shows available information of given user.") },


		{0, 0, 0, 0, 0}			/* End of list. */
};


/* this function is necessary for old bsearch-way of command parsing. leaving
   it in place for now, until we decide (see below)
*/
static int
command_compare (const void *a, const void *b)
{
	return strcasecmp (a, ((struct commands *)b)->name);
}

static struct commands *find_internal_command (char *name)
{
	int i = 0, j, diff;

	while (xc_cmds[i].name) {
		diff = 0;
		for (j=0;j<strlen(xc_cmds[i].name) && j<strlen(name);j++)
			if (tolower(xc_cmds[i].name[j]) != tolower(name[j])) diff++;
		if (!diff) break;
		i++;
	}

/* keeping the old way here until we decide which way commands will be parsed */
	/* the "-1" is to skip the NULL terminator */
	if (xc_cmds[i].name && (strlen(xc_cmds[i].name) >= strlen(name)))
		return bsearch (xc_cmds[i].name, xc_cmds, (sizeof (xc_cmds) /
				sizeof (xc_cmds[0])) - 1, sizeof (xc_cmds[0]), command_compare);
	return(NULL);
}

void
command_help (gchar *tbuf, gchar *helpcmd, int quiet)
{
	struct commands *cmd;

	/*if (plugin_show_help (sess, helpcmd))
		return;*/

	cmd = find_internal_command (helpcmd);

	if (cmd)
	{
		if (cmd->help)
		{
			g_snprintf (tbuf, 255, (_("Usage: %s")), (_(cmd->help)));
			printconsole (tbuf);
		} else
		{
			if (!quiet)
				printconsole (_("No help available for that command."));
		}
		return;
	}

	if (!quiet)
		printconsole (_("No such command."));
}

gboolean handle_command_callback (GtkWidget *emitter, gchar *cmd) {
  debug("handle_command_callback('%s')", cmd);
  handle_command(cmd);
  return TRUE;
}

/* handle a command, without the '/' prefix */

int
handle_command (char *cmd)
{
	/*struct popup *pop;
	int user_cmd = FALSE;*/
	/*GSList *list;*/
	char *word[PDIWORDS];
	char *word_eol[PDIWORDS];
	static int command_level = 0;
	struct commands *int_cmd;
	char pdibuf_static[1024];
	char tbuf_static[4096];
	char *pdibuf;
	char *tbuf;
	int len;
	int ret = TRUE;

	debug_info("cmd: '%s'", cmd);

	/* anything below MUST DEC command_level before returning */

	len = strlen (cmd);

	if (len == 0)
		return FALSE;

	if (len >= sizeof (pdibuf_static))
		pdibuf = malloc (len + 1);
	else
		pdibuf = pdibuf_static;

	if ((len * 2) >= sizeof (tbuf_static))
		tbuf = malloc ((len * 2) + 1);
	else
		tbuf = tbuf_static;

	/* split the text into words and word_eol */
	process_data_init (pdibuf, cmd, word, word_eol, TRUE);

	/* first see if it's a userCommand */
	/* list = command_list; No user commands yet
	while (list)
	{
		pop = (struct popup *) list->data;
		if (!strcasecmp (pop->name, word[1]))
		{
			user_command (sess, tbuf, pop->cmd, word, word_eol);
			user_cmd = TRUE;
		}
		list = list->next;
	}

	if (user_cmd)
		goto xit;
        */
	/* now check internal commands */

	int_cmd = find_internal_command (word[1]);

	if (int_cmd) {

	  if (int_cmd->needserver && !CONNECTED) {
	    notc_msg (); /* not connected to a server */

	  } else if ( int_cmd->needchannel && !channels ) { /* FIXME, check that user is atleast on one channel */
	    notj_msg (); /* not joined to a channel */

	  } else {
	    switch (int_cmd->callback (tbuf, word, word_eol)) {
	    case FALSE:
	      command_help (tbuf, int_cmd->name, TRUE);
	      break;
	    case 2:
	      ret = FALSE;
	      goto xit;
	    }
	  }

	} else {

	  debug_info("Unknown command '%s'", word[1] );
	  printconsole(_("No such command."));
	}

 xit:
	command_level--;

	if (pdibuf != pdibuf_static)
	  free (pdibuf);

	if (tbuf != tbuf_static)
	  free (tbuf);

	return ret;
}

void
process_data_init (char *buf, char *cmd, char *word[],
						 char *word_eol[], int handle_quotes)
{
	int wordcount = 2;
	int space = FALSE;
	int quote = FALSE;
	int j = 0;
	int len;

	word[0] = "\000\000";
	word_eol[0] = "\000\000";
	word[1] = (char *)buf;
	word_eol[1] = (char *)cmd;

	while (1)
	{
		switch (*cmd)
		{
		case 0:
		 jump:
			buf[j] = 0;
			for (j = wordcount; j < PDIWORDS; j++)
			{
				word[j] = "\000\000";
				word_eol[j] = "\000\000";
			}
			return;
		case '\042':
			if (!handle_quotes)
				goto def;
			if (quote)
				quote = FALSE;
			else
				quote = TRUE;
			cmd++;
			break;
		case ' ':
			if (!quote)
			{
				if (!space)
				{
					buf[j] = 0;
					j++;

					word[wordcount] = &buf[j];
					word_eol[wordcount] = cmd + 1;
					wordcount++;

					if (wordcount == PDIWORDS - 1)
						goto jump;

					space = TRUE;
				}
				cmd++;
				break;
			}
		default:
def:
			space = FALSE;
			len = g_utf8_skip[((unsigned char *)cmd)[0]];
			if (len == 1)
			{
				buf[j] = *cmd;
				j++;
				cmd++;
			} else
			{
				/* skip past a multi-byte utf8 char */
				memcpy (buf + j, cmd, len);
				j += len;
				cmd += len;
			}
		}
	}
}


syntax highlighted by Code2HTML, v. 0.9.1