Logo Search packages:      
Sourcecode: dctc version File versions  Download package

keyboard_master.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * keyboard_master.c: Copyright (C) Eric Prevoteau <www@a2pb.gotdns.org>
 *
 * 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.
 */
/*
$Id: keyboard_master.c,v 1.14 2004/01/09 18:16:01 ericprev Exp $
*/

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/sem.h>
#include <netdb.h>

#ifdef WITH_GCRYPT
#include <gcrypt.h>
#define MD4_DIGEST_LENGTH 16
#define MD4_ALGO(source,len,dest)   gcry_md_hash_buffer(GCRY_MD_MD4,dest,source,len)
#else
#include <openssl/md4.h>
#define MD4_ALGO MD4
#endif

#include <glib.h>

#if !(defined(BSD) && (BSD >= 199103))
       #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
       /* union semun is defined by including <sys/sem.h> */
       #else
       /* according to X/OPEN we have to define it ourselves */
       union semun {
               int val;                    /* value for SETVAL */
               struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
               unsigned short int *array;  /* array for GETALL, SETALL */
               struct seminfo *__buf;      /* buffer for IPC_INFO */
       };
       #endif
#endif

#include "dc_com.h"
#include "display.h"
#include "macro.h"
#include "main.h"
#include "var.h"
#include "mydb.h"
#include "action.h"
#include "network.h"
#include "keyboard.h"
#include "hl_locks.h"
#include "typical_action.h"
#include "key.h"
#include "gts.h"
#include "md.h"
#include "timed_out_string.h"
#include "tos_key.h"
#include "user_manage.h"
#include "gdl.h"
#include "uaddr.h"
#include "dc_manage.h"
#include "misc.h"
#include "status.h"
#include "sema.h"
#include "md_crc.h"
#include "md_db.h"
#include "dc_xfer_common.h"

/**************************************************/
/* this function is now a wrapper to GDL creation */
/*******************************************************************************************************/
/* the cmd syntax is "/DL AnickAlocalficAremoteficAfilesize", A is a byte used as separator. It can be */
/* anything except \n,\0 and space (0x20)                                                              */
/*******************************************************************************************************/
static void keyb_do_download(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **fields;
      gchar sep[2];

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /DL command (1)",NULL);
            return;
      }

      sep[0]=*t++;
      sep[1]='\0';
            
      fields=g_strsplit(t,sep,0);
      if(gchar_array_len(fields)!=4)
      {
            disp_msg(ERR_MSG,"","invalid /DL command (2)","|d",gchar_array_len(fields),"|s",t,NULL);
      }
      else
      {
            char *nick, *local, *remote;
            unsigned long int filesize;
            unsigned idx;
            int ret;

            /* check nick field */
            nick=fields[0];
            if(strlen(nick)==0)
            {
                  disp_msg(ERR_MSG,"","invalid /DL command: no nickname",NULL);
                  goto abrt;
            }

            /* check remote filename entry */
            remote=fields[2];
            if(strlen(remote)==0)
            {
                  disp_msg(ERR_MSG,"","invalid /DL command: no remote filename",NULL);
                  goto abrt;
            }

            /* check the file size field */
            if(strlen(fields[3])==0)
            {
                  disp_msg(ERR_MSG,"","invalid /DL command: no file size",NULL);
                  goto abrt;
            }
            filesize=strtoul(fields[3],NULL,10);
            if(filesize==0)
            {
                  disp_msg(ERR_MSG,"","invalid /DL command: file size is 0",NULL);
                  goto abrt;
            }

            /* and finally the local file name */
            if(strlen(fields[1])==0)
            {
                  local=strrchr(remote,'\\');
                  if(local==NULL)
                        local=remote;
                  else
                        local++;
            }
            else
                  local=fields[1];

            do
            {
                  idx=rand();
                  ret=do_gdl_new(idx,local,filesize);
                  if(ret==1)        /* GDL with same local filename */
                        goto abrt;
            } while(ret!=0);  /* retry until success */

            do_gdl_add(idx,nick,remote,filesize);
      }

      abrt:
      if(fields)
            g_strfreev(fields);
      return;
}

/***********************************************/
/* this function initiates a xdownload process */
/***********************************************/
/* the cmd syntax is "/XDL|gld_id|nickname|" */
/*********************************************/
static void keyb_do_xdownload(const char *cmd, int sck, char *input, void *xtra_param)
{
      gchar **fields;

      /* create a copy of the input string. We need it if we have to requeue the download */
#ifndef WITH_GLIB2
      fields=g_strsplit(input,"|",3);           /* GLIB2 fixed */
#else
      fields=g_strsplit(input,"|",3+1);   /* GLIB2 fix */
#endif

      if((fields[0]==NULL)||(fields[1]==NULL)||(fields[2]==NULL))
      {
            g_strfreev(fields);
            return;
      }

      if(!act_with_user_in_progress(fields[2]))
      {
            GString *action_to_do;
            GString *uad=NULL;
            /* no download with this user in progress */

            if(with_ddl)
            {     /* try to bypass the hub */
                  uad=get_uaddr_dl_addr_by_name(fields[2]);
            }

            if(uad==NULL)
            {
                  /* there is no need to test cnx_in_progress here because we always check hub_user_list */
                  /* and this list is only filled after the connection handshake */
                  if(behind_fw)
                  {     /* we are behind a firewall */
                        /* so, the remote side wont be able to contact us */
                        /* let's ask a reverse connection */
                        if(!user_in_list(hub_user_list,fields[2]))            /* if the user is not here and we are behind a firewall, we cannot do anything */
                              goto abrt;

                        LOCK_READ(user_info);
                        send_dc_line(sck,"$RevConnectToMe",nickname,fields[2],NULL);
                        UNLOCK_READ(user_info);
                        disp_msg(DEBUG_MSG,"","/XDL in $RevConnectToMe",NULL);
                  }
                  else
                  {     /* we have a full access, the remote side will be able to contact us */
                        char tmp[512];

                        sprintf(tmp,"%s:%hu",host_ip,com_port);
                        if(user_in_list(hub_user_list,fields[2]))
                              send_dc_line(sck,"$ConnectToMe",fields[2],tmp,NULL);
                        else
                        {
                              /* try a conditionnal download on other client */
                              send_dc_line_to_dctc_link(fields[2],sck,"$ConnectToMe",fields[2],tmp,NULL);

                              /* and to improve algorithm, also send a query to UNODE to try to obtain the IP from other UNODEs */
                              {
                                    GString *und;

                                    und=g_string_new("");
                                    g_string_sprintf(und,"$UADDR? %s|",fields[2]);
                                    send_str_to_unode(und);
                                    g_string_free(und,TRUE);
                              }
                        }
                        disp_msg(DEBUG_MSG,"","/XDL in $ConnectToMe",NULL);
                  }
                  action_to_do=g_string_new(input+1);       /* action to do is "XDL|gld_id|nickname" */
                  create_and_add_action_to_do(fields[2],action_to_do);        /* don't free action_to_do */
            }
            else
            {
                  action_to_do=g_string_new(input+1);       /* action to do is "XDL|gld_id|nickname" */
                  create_and_add_action_to_do(fields[2],action_to_do);        /* don't free action_to_do */

                  /* and simulate the connection */
                  uad=g_string_append_c(uad,'|');
                  uad=g_string_prepend_c(uad,' ');
                  uad=g_string_prepend(uad,fields[2]);
                  uad=g_string_prepend(uad,"$ConnectToMe ");            /* rebuild a string $ConnectToMe nick ip:port */
                  disp_msg(DEBUG_MSG,"keyb_do_xdownload","trying",uad->str,"for",fields[2],NULL);
                  connecttome_process("$ConnectToMe ",-1,uad,NULL);     /* simulate the incoming of a $ConnectToMe */

                  g_string_free(uad,TRUE);
            }
      }
      else
      {
            abrt:
            do_gdl_abort(strtoul(fields[1],NULL,10),fields[2]);
      }

      if(fields)
            g_strfreev(fields);
}


/********************************************/
/* this function initiates a search process */
/*********************************************************************************************************/
/* the cmd syntax is "/SRCH ApatternAfiletype[AsizetypeAsize]", A is a byte used as separator. It can be */
/* anything except \n,\0 and space (0x20).                                                               */
/* Pattern is the pattern to find                                                                        */
/* filetype is 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder                   */
/* a filesize can also be provided. sizetype is L if the given size is "at least", and M if the given    */
/* size it "at most". The provided size is in byte.                                                      */
/*********************************************************************************************************/
static void keyb_do_search(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *pattern;
      char *filetype;
      char *sizetype;
      char *size;
      char sep[2];
      GString *return_addr;
      
      char *tk;
      char *t;

      /* check if the delay between searchs is running */
      if(tos_entry_exists(LMTSRCH_TOSKEY,"delay",5,0))
            return;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /SRCH command (1)",NULL);
            return;
      }

      sep[0]=*t++;
      sep[1]='\0';

      /* set the pattern */
      t=strtok_r(t,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /SRCH command (2)",NULL);
            return;
      }
      pattern=t;

      /* set the filetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /SRCH command (3)",NULL);
            return;
      }
      filetype=t;
      
      /* set the sizetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            sizetype=NULL;
            size=NULL;
      }
      else
      {
            char v;

            sizetype=t;

            v=toupper(*sizetype);

            if((v!='L') &&(v!='M'))
            {
                  disp_msg(ERR_MSG,"","invalid /SRCH command (invalid size type, should be L or M)",NULL);
                  return;
            }

            /* set the size */
            t=strtok_r(NULL,sep,&tk);
            if(t==NULL)
            {
                  disp_msg(ERR_MSG,"","invalid /SRCH command (4)",NULL);
                  return;
            }
            size=t;

            if(strtoul(size,NULL,10)==0)
            {     /* if the size is 0, size doesn't matter */
                  sizetype=NULL;
                  size=NULL;
            }
      }

      /* search is supported in both active and passive mode */
      /* an active mode search replace Hub:nick by hostip:com_port */
      if(behind_fw)
      {     /* the return address */
            return_addr=g_string_new(NULL);
            LOCK_READ(user_info);
            g_string_sprintf(return_addr,"Hub:%s",nickname);
            UNLOCK_READ(user_info);
      }
      else
      {
            /* the return address */
            return_addr=g_string_new(NULL);
            LOCK_READ(user_info);
            g_string_sprintf(return_addr,"%s:%hu",host_ip,com_port);
            UNLOCK_READ(user_info);
      }

      /* build a string like "$Search Hub:nick a?b?c?d?eeeee| where */
      /* a is F if size doesn't matter, else T */
      /* b is F if size is "at least", else T (at most) */
      /* c is the size in byte */
      /* d is data type: 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder */
      /* and eeee is the pattern to find */
      {
            GString *query;

            /* build the query */
            if(sizetype==NULL)
            {
                  query=g_string_new("F?F?0");  /* size doesn't matter */
            }
            else
            {
                  query=g_string_new(NULL);
                  g_string_sprintf(query,"T?%c?%lu",
                                                                  (toupper(*sizetype)=='L')?'F':'T',              /* convert size type (at least -> F, at most -> T) */
                                                                  strtoul(size,NULL,10)
                                                                        );
            }

            g_string_sprintfa(query,"?%lu?%s",strtoul(filetype,NULL,10),pattern);

            /* and do the query */
            send_dc_line(sck,"$Search",return_addr->str,query->str,NULL);
            if(!behind_fw)
            {
                  GString *und;

                  send_dc_line_to_dctc_link(NULL,sck,"$Search",return_addr->str,query->str,NULL);

                  /* and also send the string to UNODE */
                  und=g_string_new("");
                  g_string_sprintf(und,"$Search %s %s|",return_addr->str,query->str);     /* be careful, no \n at the end here */
                  send_str_to_unode(und);
                  g_string_free(und,TRUE);
            }

            g_string_free(query,TRUE);
            g_string_free(return_addr,TRUE);
      }

      /* set search query limiter timeout */
      if(min_delay_between_search)
            add_tos_entry_v1_uniq(LMTSRCH_TOSKEY,min_delay_between_search,"delay",5,NULL,0);
}

/****************************************************/
/* this function initiates a content search process */
/*********************************************************************************************************/
/* the cmd syntax is "/SRCH Alocalfilename[AsizetypeAsize]", A is a byte used as separator. It can be    */
/* anything except \n,\0 and space (0x20).                                                               */
/* localfilename is the filename containing the data we want to find.                                    */
/* filetype is 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder                   */
/* a filesize can also be provided. sizetype is L if the given size is "at least", and M if the given    */
/* size it "at most". The provided size is in byte.                                                      */
/*********************************************************************************************************/
static void keyb_do_csearch(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *pattern;
      char *filetype;
      char *sizetype;
      char *size;
      char sep[2];
      GString *return_addr;
      
      char *tk;
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /CSRCH command (1)",NULL);
            return;
      }

      sep[0]=*t++;
      sep[1]='\0';

      /* set the pattern */
      t=strtok_r(t,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /CSRCH command (2)",NULL);
            return;
      }
      pattern=t;

      /* set the filetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /CSRCH command (3)",NULL);
            return;
      }
      filetype=t;
      
      /* set the sizetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            sizetype=NULL;
            size=NULL;
      }
      else
      {
            char v;

            sizetype=t;

            v=toupper(*sizetype);

            if((v!='L') &&(v!='M'))
            {
                  disp_msg(ERR_MSG,"","invalid /CSRCH command (invalid size type, should be L or M)",NULL);
                  return;
            }

            /* set the size */
            t=strtok_r(NULL,sep,&tk);
            if(t==NULL)
            {
                  disp_msg(ERR_MSG,"","invalid /CSRCH command (4)",NULL);
                  return;
            }
            size=t;

            if(strtoul(size,NULL,10)==0)
            {     /* if the size is 0, size doesn't matter */
                  sizetype=NULL;
                  size=NULL;
            }
      }

      /* search is supported in both active and passive mode */
      /* an active mode search replace Hub:nick by hostip:com_port */
      if(behind_fw)
      {     /* the return address */
            return_addr=g_string_new(NULL);
            LOCK_READ(user_info);
            g_string_sprintf(return_addr,"Hub:%s",nickname);
            UNLOCK_READ(user_info);
      }
      else
      {
            /* the return address */
            return_addr=g_string_new(NULL);
            LOCK_READ(user_info);
            g_string_sprintf(return_addr,"%s:%hu",host_ip,com_port);
            UNLOCK_READ(user_info);
      }

      /* build a string like "$Search Hub:nick a?b?c?d?eeeee| where */
      /* a is F is size doesn't matter, else T */
      /* b is F if size is "at least", else T (at most) */
      /* c is the size in byte */
      /* d is data type: 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder */
      /* and eeee is the pattern to find */
      {
            GString *query;
            unsigned char md5sum[MD5SUMLEN];

            
            /* build the query */
            if(sizetype==NULL)
            {
                  query=g_string_new("F");

                  if(!md5sum_of_file(pattern,md5sum))
                  {
                        char txt_md[3*MD5SUMLEN+1];
                        md5tostr(md5sum,txt_md);
                        g_string_sprintfa(query,".%s",txt_md);
      
                        /* add a tos entry for this search */
                        add_tos_entry(CSRCH_TOSKEY,40,pattern,strlen(pattern)+1,md5sum,MD5SUMLEN);
                  }

                  g_string_sprintfa(query,"?F?0");    /* size doesn't matter */
            }
            else
            {
                  query=g_string_new("T");
                  if(!md5sum_of_file(pattern,md5sum))
                  {
                        char txt_md[3*MD5SUMLEN+1];
                        md5tostr(md5sum,txt_md);
                        g_string_sprintfa(query,".%s",txt_md);
      
                        /* add a tos entry for this search */
                        add_tos_entry(CSRCH_TOSKEY,40,pattern,strlen(pattern)+1,md5sum,MD5SUMLEN);
                  }

                  g_string_sprintfa(query,"?%c?%lu",
                                                                  (toupper(*sizetype)=='L')?'F':'T',              /* convert size type (at least -> F, at most -> T) */
                                                                  strtoul(size,NULL,10)
                                                                        );
            }


            g_string_sprintfa(query,"?%lu?",strtoul(filetype,NULL,10));

            g_string_sprintfa(query,"%s",pattern);

            /* and do the query */
            send_dc_line(sck,"$Search",return_addr->str,query->str,NULL);

            g_string_free(query,TRUE);
            g_string_free(return_addr,TRUE);
      }
}

/******************************************************/
/* this function initiates a multi hub search process */
/*********************************************************************************************************/
/* the cmd syntax is "/MSRCH ApatternAfiletype[AsizetypeAsize]", A is a byte used as separator. It can be*/
/* anything except \n,\0 and space (0x20).                                                               */
/* Pattern is the pattern to find                                                                        */
/* filetype is 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder                   */
/* a filesize can also be provided. sizetype is L if the given size is "at least", and M if the given    */
/* size it "at most". The provided size is in byte.                                                      */
/*********************************************************************************************************/
static void keyb_do_msearch(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *pattern;
      char *filetype;
      char *sizetype;
      char *size;
      char sep[2];
      GString *return_addr;
      
      char *tk;
      char *t;

      if(behind_fw)
      {
            disp_msg(ERR_MSG,"","you cannot user /MSRCH command in passive mode",NULL);
      }

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /MSRCH command (1)",NULL);
            return;
      }

      sep[0]=*t++;
      sep[1]='\0';

      /* set the pattern */
      t=strtok_r(t,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /MSRCH command (2)",NULL);
            return;
      }
      pattern=t;

      /* set the filetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /MSRCH command (3)",NULL);
            return;
      }
      filetype=t;
      
      /* set the sizetype */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            sizetype=NULL;
            size=NULL;
      }
      else
      {
            char v;

            sizetype=t;

            v=toupper(*sizetype);

            if((v!='L') &&(v!='M'))
            {
                  disp_msg(ERR_MSG,"","invalid /MSRCH command (invalid size type, should be L or M)",NULL);
                  return;
            }

            /* set the size */
            t=strtok_r(NULL,sep,&tk);
            if(t==NULL)
            {
                  disp_msg(ERR_MSG,"","invalid /MSRCH command (4)",NULL);
                  return;
            }
            size=t;

            if(strtoul(size,NULL,10)==0)
            {     /* if the size is 0, size doesn't matter */
                  sizetype=NULL;
                  size=NULL;
            }
      }

      /* To start a multi-hub search, active mode is required */
      {
            /* the return address */
            return_addr=g_string_new(NULL);
            LOCK_READ(user_info);
            g_string_sprintf(return_addr,"%s:%hu",host_ip,com_port);
            UNLOCK_READ(user_info);
      }

      /* build a string like "$MultiSearch host:ip a?b?c?d?eeeee| where */
      /* a is F is size doesn't matter, else T */
      /* b is F if size is "at least", else T (at most) */
      /* c is the size in byte */
      /* d is data type: 1=any,2=audio,3=compressed,4=document,5=exe,6=picture,7=videos,8=folder */
      /* and eeee is the pattern to find */
      {
            GString *query;

            /* build the query */
            if(sizetype==NULL)
            {
                  query=g_string_new("F?F?0");  /* size doesn't matter */
            }
            else
            {
                  query=g_string_new(NULL);
                  g_string_sprintf(query,"T?%c?%lu",
                                                                  (toupper(*sizetype)=='L')?'F':'T',              /* convert size type (at least -> F, at most -> T) */
                                                                  strtoul(size,NULL,10)
                                                                        );
            }

            g_string_sprintfa(query,"?%lu?%s",strtoul(filetype,NULL,10),pattern);

            /* and do the query */
            send_dc_line(sck,"$MultiSearch",return_addr->str,query->str,NULL);

            g_string_free(query,TRUE);
            g_string_free(return_addr,TRUE);
      }
}

/**********************************************************/
/* append the string to_add at the end of the GString *in */
/* \r of to_add are converted in \n                       */
/**********************************************************/
GString *untranslate_char(GString *in,char *to_add)
{
      while(*to_add)
      {
            if(*to_add=='\r')
                  in=g_string_append(in,"\r\n");
            else
                  in=g_string_append_c(in,*to_add);
            to_add++;
      }
      return in;
}

/***************************************************/
/* this function send a message on the global chat */
/***************************************************/
/* the cmd syntax is "/CHAT msg" */
/*********************************/
static void keyb_do_chat(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      GString *me;

      t=input+strlen(cmd);

      me=g_string_new(NULL);
      LOCK_READ(user_info);
      g_string_sprintfa(me,"<%s> ",nickname);
      UNLOCK_READ(user_info);

      me=untranslate_char(me,t);

      send_dc_line(sck,me->str,NULL);
      g_string_free(me,TRUE);
}
      
/***************************************************/
/* this function send a private message to someone */
/**************************************************************/
/* the cmd syntax is "/PRIV AnickAmsg" where A is a separator */
/* nick the destination nickname and msg the message. It is   */
/* not possible to send message containing \n                 */
/**************************************************************/
static void keyb_do_priv_chat(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      GString *me;
      char sep[2];
      char *nick;
      char *msg;
      char *tk;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /PRIV command (1)",NULL);
            return;
      }

      sep[0]=*t++;
      sep[1]='\0';

      /* set the nickname */
      t=strtok_r(t,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /PRIV command (2)",NULL);
            return;
      }
      nick=t;

      /* set the message */
      t=strtok_r(NULL,sep,&tk);
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /PRIV command (3)",NULL);
            return;
      }
      msg=t;

      me=g_string_new(NULL);
      LOCK_READ(user_info);
      g_string_sprintfa(me,"$<%s> ",nickname);        /* we should had a $ before the message */
      me=untranslate_char(me,msg);
      if(user_in_list(hub_user_list,nick))
            send_dc_line(sck,"$To:",nick,"From:",nickname,me->str,NULL);
      else
            send_dc_line_to_dctc_link(nick,sck,"$To:",nick,"From:",nickname,me->str,NULL);
      UNLOCK_READ(user_info);

      /* produce a local echo of the message, because the hub doesn't */
      g_string_sprintf(me,"<%s> %s",nickname,msg);
      disp_msg(PRIV_MSG,NULL,nick,me->str,NULL);            

      g_string_free(me,TRUE);
}

/*******************************************************/
/* this function displays the list of all running xfer */
/*******************************************************/
/* the cmd syntax is "/XFER" */
/*****************************/
static void keyb_do_xfer(const char *cmd, int sck, char *input, void *xtra_param)
{
      int i;
      char tmp[512];

      disp_msg(XFER_LST_BEGIN,NULL,NULL);

      if(!strcmp(cmd,"/XFER"))
      {
            /* first, running xfers */
            G_LOCK(waiting_action);

            for(i=0;i<waiting_action->len;i++)
            {
                  WAIT_ACT *nw;

                  nw=(WAIT_ACT*)(g_ptr_array_index(waiting_action,i));
                  if(nw==NULL)
                        continue;

                  sprintf(tmp,"%lu",(unsigned long)(nw->thread_id));
                  if(nw->remote_nick==NULL)
                  {
                        disp_msg(XFER_LST_R,NULL,tmp,"",NULL);
                  }
                  else
                  {
                        if((nw->disp_info==NULL)||(nw->disp_info->len==0))
                              disp_msg(XFER_LST_R,NULL,tmp,nw->remote_nick->str,"Idle",NULL);
                        else
                              disp_msg(XFER_LST_R,NULL,tmp,nw->remote_nick->str,nw->disp_info->str,NULL);
                  }
            }
            G_UNLOCK(waiting_action);
      
            /* after, queued xfers */
            G_LOCK(waiting_revcon);
            for(i=0;i<waiting_revcon->len;i++)
            {
                  WAIT_REVCON *nw;
      
                  nw=(WAIT_REVCON*)(g_ptr_array_index(waiting_revcon,i));
      
                  disp_msg(XFER_LST_Q,NULL,nw->remote_nick->str,nw->action_to_do->str,NULL);
            }
      
            G_UNLOCK(waiting_revcon);
      }

      /* sim input list is no more reachable */
      /* instead, CMD_KB contains GTS */
      list_gts_content();

      disp_msg(XFER_LST_END,NULL,NULL);
}

/************************************************************/
/* check if the given string contains invalid DC characters */
/* invalid characters are replaced by _ (like DC)           */
/************************************************************/
static void check_and_adapt_string(char *string)
{
   while(*string!='\0')
   {
      if((*string=='|')||(*string=='$'))
      {
                  *string='_';
      }
      string++;
   }
}

/**************************************************************/
/* this function changes the current download directory (cwd) */
/**************************************************************/
/* the cmd syntax is "/LPATH directory"   */
/******************************************/
static void keyb_do_lpath(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /LPATH command (1)",NULL);
            return;
      }

      /* change current directory to the given directory */
      if(chdir(t))
      {
            char *s=strerror(errno);
            disp_msg(ERR_MSG,"","invalid /LPATH command",t,s,NULL);
      }
}

/***********************************************/
/* this function modifies download slot number */
/***********************************************/
/* the cmd syntax is "/SLOT number" */
/************************************/
static void keyb_do_slot(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      int slot;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /SLOT command (1)",NULL);
            return;
      }

      /* modify the number of slot */
      slot=atoi(t);
      if(slot<0)
            slot=0;

      set_number_of_ul_slot(bl_semid,slot);
}

/******************************************/
/* this function modifies active com port */
/******************************************/
/* the cmd syntax is "/PORT number" */
/************************************/
static void keyb_do_port(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned short slot;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /PORT command (1)",NULL);
            return;
      }

      /* modify the number of slot */
      slot=strtoul(t,NULL,10);
      if(slot==0)
      {
            disp_msg(ERR_MSG,"","invalid /PORT command (2), must use a port number between 1 and 65535",NULL);
            return;
      }

      if(com_port==slot)            /* new port is same as old port */
            return;

      com_port=slot;

      if(in_sck==-1)                      /* no com port, nothing to do */
            return;

      do
      {
            shutdown(in_sck,2);           /* close old com port */
            close(in_sck);

            do
            {
                  in_sck=_x_tcp(com_port);/* and create a new one */
                  if(in_sck==-1)
                        com_port++;
            }while(in_sck==-1);

            listen(in_sck,64);

            /* same thing to do for the search port */
            shutdown(srch_sck,2);
            close(srch_sck);
            srch_sck=_x_udp(com_port,0);
            if(srch_sck==-1)
                  com_port++;
      }while(srch_sck==-1);
      
      set_tos_sock(srch_sck,udp_tos);

      listen(in_sck,64);
}

/**********************************/
/* this function kills a transfer */
/************************************/
/* the cmd syntax is "/KILL number" */
/************************************/
static void keyb_do_kill(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      pthread_t id;
      int i;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /KILL command (1)",NULL);
            return;
      }

      /* modify our nick name locally */
      id=strtoul(t,NULL,10);

      /* to shutdown a thread, it is very easy, we just close the socket it uses */
      /* thus, it will abort himself, freeing all ressources it allocates */
      G_LOCK(waiting_action);
      for(i=0;i<waiting_action->len;i++)
      {
            WAIT_ACT *ptr;

            ptr=g_ptr_array_index(waiting_action,i);
            if(ptr->thread_id==id)
            {
                  shutdown(ptr->sock_fd,2);           /* shutdown the socket (in both directions) */
                  ptr->running=3;                           /* task is killed */
                  break;
            }
      }
      G_UNLOCK(waiting_action);
}

/****************************************************/
/* this function changes the Upload bandwidth limit */
/****************************************************/
/* the cmd syntax is "/xBL number" */
/***********************************/
static void keyb_do_xbl(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      int xbl;
      union semun v;
      int sem_num;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /xBL command (1)",NULL);
            return;
      }

      /* modify our nick name locally */
      xbl=atoi(t);

      if(xbl<1)
            xbl=INT_MAX;

      sem_num=GPOINTER_TO_INT(xtra_param);

      v.val=xbl;
      if(semctl(bl_semid,sem_num,SETVAL,v)==-1)
            disp_msg(ERR_MSG,"","invalid /xBL command (1)",strerror(errno),NULL);
}

/************************************************/
/* this function changes the reconnection delay */
/************************************************/
/* the cmd syntax is "/RECOND number" */
/**************************************/
static void keyb_do_recond(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /RECOND command (1)",NULL);
            return;
      }

      /* modify the recon delay */
      recon_delay=atoi(t);

      if((recon_delay<30)||(recon_delay>1800))
            recon_delay=30;
}

/*****************************************************/
/* this function changes the GDL autoscan port range */
/*****************************************************/
/* the cmd syntax is "/GDLASPORTS number,number" */
/*************************************************/
static void keyb_do_gdl_as_ports(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int v1,v2;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /GDLASPORTS command (1)",NULL);
            return;
      }

      if(sscanf(t,"%u,%u",&v1,&v2)!=2)
      {
            disp_msg(ERR_MSG,"","invalid /GDLASPORTS parameters (2 required)",NULL);
            return;
      }

      if(v1==0)
      {     /* no range */
            gdl_as_port_range[0]=gdl_as_port_range[1]=0;
      }
      else
      {
            if(v1==v2)
            {     /* only one port */
                  if(v1<65536)
                  {
                        gdl_as_port_range[0]=v1;
                        gdl_as_port_range[1]=0;
                  }
                  else
                  {
                        disp_msg(ERR_MSG,"","invalid /GDLASPORTS parameters, the port must be smaller than 65536",NULL);
                        return;
                  }
            }
            else
            {
                  if(v1<v2)
                  {
                        if(v2<65536)            /* because v1<v2, if v2<65536 then v1<65536 */
                        {
                              gdl_as_port_range[0]=v1;
                              gdl_as_port_range[1]=v2;
                        }
                        else
                        {
                              disp_msg(ERR_MSG,"","invalid /GDLASPORTS parameters, ports must be smaller than 65536",NULL);
                              return;
                        }
                  }
                  else
                  {
                        disp_msg(ERR_MSG,"","invalid /GDLASPORTS parameters, the second port must be greater than the first",NULL);
                        return;
                  }
            }
      }
}

/*************************************************/
/* this function kills a queued keyboard command */
/*************************************************/
/* the cmd syntax is "/KILLKB string" */
/**************************************/
static void keyb_do_killkb(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int i;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /KILLKB command (1)",NULL);
            return;
      }

      /* t is the string to delete */

      /* removing a KBcmd, is quite easy */
      G_LOCK(sim_input);
      for(i=0;i<sim_input->len;i++)
      {
            SIM_INPUT *ptr;

            ptr=&(g_array_index(sim_input,SIM_INPUT,i));

            if(!strcmp(ptr->keyb_string->str,t))
            {
                  /* same string */
                  g_string_free(ptr->keyb_string,TRUE);                       /* free the command string */
                  sim_input=g_array_remove_index(sim_input,i);          /* and remove the entry */
                  break;
            }
      }
      G_UNLOCK(sim_input);
}

/*************************************************/
/* this function kills a queued keyboard command */
/*************************************************/
/* the cmd syntax is "/KILLKBN number" */
/***************************************/
static void keyb_do_killkbn(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned long id;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /KILLKBN command (1)",NULL);
            return;
      }

      /* t is the string to delete */
      id=strtoul(t,NULL,10);

      /* removing a KBcmd, is quite easy */
      /* KILLKBN now removes entry in GTS */
      delete_gts_entry_by_id(id);
}

/*****************************************************************************/
/* this function provides a generic handling of unsigned int setting command */
/*****************************************************************************/
/* the cmd syntax is "/xxxxx number" */
/*************************************/
static void keyb_do_set_unsigned_int(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int *id=xtra_param;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid",cmd,"command (1)",NULL);
            return;
      }

      /* t is the string to delete */
      (*id)=strtoul(t,NULL,10);
}

/**************************************/
/* this function modifies our host IP */
/**************************************************************/
/* the cmd syntax is "/IP [host_ip]" (the space is mandatory) */
/**************************************************************/
static void keyb_do_ip(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      char *nw,*old;
      GString *hip;
      gchar *ohip;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

#if 0
      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /IP command (1)",NULL);
            return;
      }
#endif

      hip=get_default_host_ip(t,dynamic_ip_flag);
      if(hip==NULL)
      {
            disp_msg(ERR_MSG,"","invalid /IP command (2)",NULL);
            return;
      }

      /* modify our IP */
      nw=strdup(t);
      LOCK_WRITE(user_info);
      old=org_host_ip;
      org_host_ip=nw;
      ohip=host_ip;
      host_ip=hip->str;
      UNLOCK_WRITE(user_info);
      if(old)
            free(old);
      if(ohip)
            g_free(ohip);
      g_string_free(hip,FALSE);           /* free GString structure, not gstring content */
}

/*********************************************/
/* enable active mode (=not behind firewall) */
/*********************************************/
/* the cmd syntax is "/ACTIVE" */
/*******************************/
static void keyb_do_active(const char *cmd, int sck, char *input, void *xtra_param)
{
      if(socks_ip!=NULL)
      {
            disp_msg(ERR_MSG,"","You are using a SOCKS proxy, active mode is not possible",NULL);
            return;
      }
      /* a function very hard to code */
      behind_fw=0;

      /* check if the com port exists, if not, create it */
      if(in_sck==-1)
      {
            in_sck=_x_tcp(com_port);
            listen(in_sck,64);
      }

      if(srch_sck==-1)
      {
            srch_sck=_x_udp(com_port,0);
            set_tos_sock(srch_sck,udp_tos);
            listen(srch_sck,64);
      }
}

/*******************************************/
/* enable passive mode (= behind firewall) */
/*******************************************/
/* the cmd syntax is "/PASSIVE" */
/********************************/
static void keyb_do_passive(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      behind_fw=1;
}

/******************************************/
/* get the list of users currently online */
/******************************************/
/* the cmd syntax is "/ULIST" */
/******************************/
static void keyb_do_ulist(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      /* but why doing something simple when we can doing something complex */
      if( ((hub_user_list==NULL)||(hub_user_list->len==0)) ||
             ((hub_op_list==NULL)||(hub_op_list->len==0)) ||
            (!strcmp(cmd,"/ULIST_FORCE")) )                             /* force list refresh */
      {
            /* empty list => proceed as usual */
            if(hub_logged)
                  send_dc_line(sck,"$GetNickList",NULL);
      }
      else
      {
            if(disp_user)
            {
                  int i;
                  char *t;
                  for(i=0;i<hub_user_list->len;i++)
                  {
                        t=g_ptr_array_index(hub_user_list,i);
                        if(t!=NULL)
                              disp_msg(USER_MSG,NULL,t,NULL);
                  }
      
                  for(i=0;i<hub_op_list->len;i++)
                  {
                        t=g_ptr_array_index(hub_op_list,i);
                        if(t!=NULL)
                              disp_msg(OP_MSG,NULL,t,NULL);
                  }
            }
      }
}

/****************************/
/* get an user informations */
/***********************************/
/* the cmd syntax is "/UINFO nick" */
/***********************************/
static void keyb_do_uinfo(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      GString *ui;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /UINFO command (1)",NULL);
            return;
      }

      check_and_adapt_string(t);

      ui=get_cached_user_uinfo(t);
      if(ui==NULL)
      {
            /* do the query */
            LOCK_READ(user_info);
            send_dc_line(sck,"$GetINFO",t,nickname,NULL);
            UNLOCK_READ(user_info);
      }
      else
      {
            if(disp_user)
                  disp_msg(USER_INFO_MSG,NULL,ui->str,NULL);
            g_string_free(ui,TRUE);
      }
}

/****************************/
/* get an user informations */
/********************************/
/* the cmd syntax is "/LS nick" */
/*********************************************************************************************************/
/* xtra_param usage: if xtra_param==NULL, normal task is performed                                       */
/*                   else, xtra_param is a WAIT_REVCON **. It must be initialized to NULL before calling */
/*                   after, instead of starting the command, it is the wait_revcon                       */
/*********************************************************************************************************/
static void keyb_do_ls(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *nick;
      GString *action_to_do;
      GString *uad=NULL;

      nick=input+strlen(cmd);
      SKIP_SPACE(nick)

      if(*nick=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /LS command (1)",NULL);
            return;
      }

      check_and_adapt_string(nick);

      if(xtra_param!=NULL)
            goto indirect_call;

      if(!act_with_user_in_progress(nick))
      {
            /* no download with this user in progress */
            if(with_ddl)
            {     /* try to bypass the hub */
                  uad=get_uaddr_dl_addr_by_name(nick);
            }

            if((uad==NULL)&&(cnx_in_progress==0))
            {
                  if(behind_fw)
                  {     /* we are behind a firewall */
                        /* so, the remote side wont be able to contact us */
                        /* let's ask a reverse connection */
                        LOCK_READ(user_info);
                        send_dc_line(sck,"$RevConnectToMe",nickname,nick,NULL);
                        UNLOCK_READ(user_info);
                        disp_msg(INFO_MSG,"","/LS in $RevConnectToMe",NULL);
                  }
                  else
                  {     /* we have a full access, the remote side will be able to contact us */
                        char tmp[512];
      
                        sprintf(tmp,"%s:%hu",host_ip,com_port);
                        send_dc_line(sck,"$ConnectToMe",nick,tmp,NULL);
                        disp_msg(INFO_MSG,"","/LS in $ConnectToMe",NULL);
                  }
            }
            indirect_call:

            action_to_do=g_string_new("LS/");
            if(xtra_param==NULL)
                  create_and_add_action_to_do(nick,action_to_do);       /* don't free action_to_do */
            else
                  *(WAIT_REVCON**)xtra_param=create_action_to_do(nick,action_to_do);    /* don't free action_to_do */

            if((xtra_param==NULL)         /* indirect_call should not go here */
                  &&(uad!=NULL))                      /* and known address */
            {
                  /* now, the task is queued, let's try to start the download thread */
                  uad=g_string_append_c(uad,'|');
                  uad=g_string_prepend_c(uad,' ');
                  uad=g_string_prepend(uad,nick);
                  uad=g_string_prepend(uad,"$ConnectToMe ");            /* rebuild a string $ConnectToMe nick ip:port */
                  disp_msg(DEBUG_MSG,"keyb_do_ls","trying",uad->str,"for",nick,NULL);
                  connecttome_process("$ConnectToMe ",-1,uad,NULL);     /* simulate the incoming of a $ConnectToMe */
            }

            if(uad!=NULL)
                  g_string_free(uad,TRUE);
      }
      else
      {
            /* a download with this user still is in progress */
            /* we will queue this new action */

            /* wait 30 seconds before retrying */
            /* we just requeue the query as is */
            if(add_gts_entry(nick,input,30))
                  add_new_sim_input(30,input);        /* fail to queue in the GTS, use sim_input instead */
      }
}

/********************************************************/
/* a new incoming client says hello to all running ones */ 
/********************************************************/
/* the cmd syntax is "/HELLODCTC" */
/**********************************/
static void keyb_do_hellodctc(const char *cmd, int sck, char *input, void *xtra_param)
{
      refresh_known_client_list(TRUE);
}

/****************************/
/* force the client to quit */
/*****************************/
/* the cmd syntax is "/QUIT" */
/*****************************/
static void keyb_do_quit(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */

      if(!strcmp(cmd,"/FORCEQUIT"))
            force_quit=1;

      shutdown(sck,2);                    /* closing the socket is a quite good method :) */
      hub_disconnect(DO_EXIT);
}

/************************/
/* toggle download flag */
/*****************************/
/* the cmd syntax is "/DLON" */
/*****************************/
static void keyb_do_dlon(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      LOCK_WRITE(user_info);
      dl_on=1;
      UNLOCK_WRITE(user_info);
}

/************************/
/* toggle download flag */
/******************************/
/* the cmd syntax is "/DLOFF" */
/******************************/
static void keyb_do_dloff(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      LOCK_WRITE(user_info);
      dl_on=0;
      UNLOCK_WRITE(user_info);
}

/******************/
/* set (int) flag */
/******************/
static void keyb_do_set_int_flag(const char *cmd, int sck, char *input, void *xtra_param)
{
      *((int*)xtra_param)=1;
}

/********************/
/* unset (int) flag */
/********************/
static void keyb_do_unset_int_flag(const char *cmd, int sck, char *input, void *xtra_param)
{
      *((int*)xtra_param)=0;
}

/*******************/
/* set (int) value */
/*******************/
static void keyb_do_set_int_value(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid set_int_value command (1)",NULL);
            return;
      }

      *((int*)xtra_param)=atoi(t);
}


#if 0
/********************/
/* toggle done flag */
/*****************************/
/* the cmd syntax is "/DONE" */
/*****************************/
static void keyb_do_done(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      when_done=1;
}

/********************/
/* toggle done flag */
/*******************************/
/* the cmd syntax is "/UNDONE" */
/*******************************/
static void keyb_do_undone(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      when_done=0;
}

/************************/
/* toggle with_ddl flag */
/*****************************/
/* the cmd syntax is "/DDL" */
/*****************************/
static void keyb_do_ddl(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      with_ddl=1;
}

/************************/
/* toggle with_ddl flag */
/******************************/
/* the cmd syntax is "/NODDL" */
/******************************/
static void keyb_do_noddl(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      with_ddl=0;
}

/*********************************/
/* toggle follow_force_move flag */
/*************************************/
/* the cmd syntax is "/FOLLOW_FORCE" */
/*************************************/
static void keyb_do_follow_force(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      follow_force_move=1;
}

/*********************************/
/* toggle follow_force_move flag */
/***************************************/
/* the cmd syntax is "/UNFOLLOW_FORCE" */
/***************************************/
static void keyb_do_unfollow_force(const char *cmd, int sck, char *input, void *xtra_param)
{
      /* a function very hard to code */
      follow_force_move=0;
}
#endif


/*****************************/
/* disable logging into file */
/******************************/
/* the cmd syntax is "/NOLOG" */
/******************************/
static void keyb_do_nolog(const char *cmd, int sck, char *input, void *xtra_param)
{
      change_logfile(NULL);
}

/*******************************/
/* change the current log file */
/*************************************/
/* the cmd syntax is "/LOG filename" */
/*************************************/
static void keyb_do_log(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /LOG command (1)",NULL);
            return;
      }

      change_logfile(t);
}

/*********************************/
/* change the current TOS values */
/******************************************/
/* the cmd syntax is "/TOS hub,udp,dl,ul" */
/******************************************/
static void keyb_do_tos(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int tmp_hub_tos,tmp_udp_tos,tmp_dl_tos,tmp_ul_tos;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /TOS command (1)",NULL);
            return;
      }

      if(sscanf(t,"%u,%u,%u,%u",&tmp_hub_tos,&tmp_udp_tos,&tmp_dl_tos,&tmp_ul_tos)==4)
      {
            hub_tos=tmp_hub_tos;
            udp_tos=tmp_udp_tos;
            dl_tos=tmp_dl_tos;
            ul_tos=tmp_ul_tos;

            set_tos_sock(main_sck,hub_tos);     /* hub connection socket */
            set_tos_sock(srch_sck,udp_tos);     /* search socket */

            unode_alter_socket_tos();
            gdl_alter_socket_tos();
      }
}


/*********************************/
/* disable ERR logging into file */
/*********************************/
/* the cmd syntax is "/NOERRLOG" */
/*********************************/
static void keyb_do_noerrlog(const char *cmd, int sck, char *input, void *xtra_param)
{
      change_errlogfile(NULL);
}

/***********************************/
/* change the current ERR log file */
/****************************************/
/* the cmd syntax is "/ERRLOG filename" */
/****************************************/
static void keyb_do_errlog(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /ERRLOG command (1)",NULL);
            return;
      }

      change_errlogfile(t);
}

/*************************/
/* toggle the debug mode */
/******************************/
/* the cmd syntax is "/DEBUG" */
/******************************/
static void keyb_do_debug(const char *cmd, int sck, char *input, void *xtra_param)
{
      const char *t;

      if(debug_mode)
      {
            debug_mode=0;
            t="Debug is off";
      }
      else
      {
            debug_mode=1;
            t="Debug is on";
      }

      disp_msg(INFO_MSG,NULL,t,NULL);
}

/*********************************************************/
/* update the IP of the client if dynamic IP flag is set */
/*********************************************************/
void update_client_ip(void)
{
      if(dynamic_ip_flag)
      {
      GString *hip;

      hip=get_default_host_ip(org_host_ip,dynamic_ip_flag);
      if(hip!=NULL)
      {
                  char *old_host_ip=host_ip;

                  /* substitute current host_ip value by the new one */
                  LOCK_WRITE(user_info);
            host_ip=hip->str;
                  UNLOCK_WRITE(user_info);

                  g_free(old_host_ip);

            g_string_free(hip,FALSE);
      }
      }
}

/***********************/
/* display the hubname */
/********************************/
/* the cmd syntax is "/HUBNAME" */
/********************************/
static void keyb_do_hubname(const char *cmd, int sck, char *input, void *xtra_param)
{
      if(hubname==NULL)
      {     /* no hubname yet available ? */
            add_new_sim_input(1,"/HUBNAME");
      }
      else
            disp_msg(HUBNAME_MSG,NULL,hubname->str,NULL);
}

static void check_min_delay_between_search_value(const char *var_name, int *var_var, char *wanted_value)
{
      int val;
      if(wanted_value==NULL)
            return;

      val=atoi(wanted_value);
      if(val<0)
      {
            disp_msg(HUBNAME_MSG,NULL,"'min_delay_between_search' value must be >=0",NULL);
      }
      else
      {
            if(val<min_delay_between_search)
            {
                  /* if the new value is smaller than the current one */
                  if(tos_entry_exists(LMTSRCH_TOSKEY,"delay",5,0))
                  {     /* and a delay is already running */
                        /* the timeout is set to the new value */
                        /* (this avoids the problem of waiting too long time if the previous timeout was erroneously set to a too great value) */
                        add_tos_entry_v1_uniq(LMTSRCH_TOSKEY,val,"delay",5,NULL,0);
                  }
            }
            min_delay_between_search=val;
      }
}

/****************************/
/* list of "int" typed flag */
/****************************/
static struct
{
      const char *var_name;
      int *var_var;
      int side_effect;              /* if side_effect ==0, the flag does not require extra computation (==it can be used by /DFLAG) */
      void (*side_effect_fnc)(const char *var_name, int *var_var, char *wanted_value);    /* if side_effect==1 and a function is given, the flag can by used by /DFLAG */
} tbl_d_flag[]={
                                    {"dl_on",&dl_on,1,NULL},
                                    {"behind_fw",&behind_fw,1,NULL},
                                    {"when_done",&when_done,0,NULL},
                                    {"follow_force_move",&follow_force_move,0,NULL},
                                    {"with_ddl",&with_ddl,0,NULL},
                                    {"grab_ban_ip",&grab_ban_ip,0,NULL},
                                    {"abort_upload_when_user_leaves",&abort_upload_when_user_leaves,0,NULL},
                                    {"hide_kick",&hide_kick,0,NULL},
                                    {"lazy_key_check",&with_lazy_key_check,0,NULL},
                                    {"with_incoming_wake_up",&with_incoming_wake_up,0,NULL},
                                    {"with_sr_wake_up",&with_sr_wake_up,0,NULL},
                                    {"min_gdl_wake_up_delay",&min_gdl_wake_up_delay,0,NULL},
                                    {"dynamic_ip",&dynamic_ip_flag,0,NULL},
                                    {"max_dl_per_user",&max_dl_per_user,0,NULL},
                                    {"min_delay_between_search",&min_delay_between_search,1,check_min_delay_between_search_value},
                                    {"sharelist_dl",&sharelist_dl_wo_slot,0,NULL},
                                    {"fake_dcpp_client",&fake_dcpp_client,0,NULL},
                                    {"disp_user",&disp_user,0,NULL},
                                    {NULL,NULL,0,NULL}
                              };

/********************************/
/* list of "GString" typed flag */
/********************************/
static struct
{
      const char *var_name;
      GString **var_var;
} tbl_gs_flag[]={
                                    {"fake_dcpp_version",&fake_dcpp_version},
                                    {NULL,NULL}
                              };

static void keyb_do_dflag_set(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 2 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t," ",1);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t," ",1+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL))
            {
                  int i=0;
                  int found=0;
                  
                  while(tbl_d_flag[i].var_name!=NULL)
                  {
                        if(!strcmp(tbl_d_flag[i].var_name,arr[0]))
                        {
                              found=1;

                              if(tbl_d_flag[i].side_effect)
                              {
                                    if(tbl_d_flag[i].side_effect_fnc==NULL)
                                          disp_msg(ERR_MSG,"","invalid /DFLAG command, this flag is not usable using this command",NULL);
                                    else
                                    {
                                          (tbl_d_flag[i].side_effect_fnc)(tbl_d_flag[i].var_name,tbl_d_flag[i].var_var,arr[1]);
                                    }
                              }
                              else
                              {
                                    *(tbl_d_flag[i].var_var)=atoi(arr[1]);
                              }
                              break;
                        }
                        i++;
                  }

                  if(!found)
                  {
                        i=0;
                        while(tbl_gs_flag[i].var_name!=NULL)
                        {
                              if(!strcmp(tbl_gs_flag[i].var_name,arr[0]))
                              {
                                    found=1;
                                    if(*(tbl_gs_flag[i].var_var)==NULL)
                                          *(tbl_gs_flag[i].var_var)=g_string_new(arr[1]);
                                    else
                                          *(tbl_gs_flag[i].var_var)=g_string_assign(*(tbl_gs_flag[i].var_var),arr[1]);
                                    break;
                              }
                        }

                        if(!found)
                              disp_msg(ERR_MSG,"","invalid /DFLAG command, unknown flag name",NULL);
                  }
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /DFLAG command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/**************************************************/
/* retrieve some internal variables on the client */
/**************************************************/
/* the cmd syntax is "/VARS" */
/*****************************/
static void keyb_do_vars(const char *cmd, int sck, char *input, void *xtra_param)
{
      char buf[5120];
      union semun v;

      LOCK_READ(user_info);

      disp_msg(VAR_MSG,NULL,"version",VERSION,NULL);
      disp_msg(VAR_MSG,NULL,"nickname",nickname,NULL);
      disp_msg(VAR_MSG,NULL,"user_desc",user_desc,NULL);
      disp_msg(VAR_MSG,NULL,"cnx_type",cnx_type,NULL);
      disp_msg(VAR_MSG,NULL,"email",email,NULL);

      sprintf(buf,"%lf",offset_sizeof_data);
      disp_msg(VAR_MSG,NULL,"offset",buf,NULL);

      disp_msg(VAR_MSG,NULL,"hostip",org_host_ip,NULL);
      disp_msg(VAR_MSG,NULL,"curhostip",host_ip,NULL);

      disp_msg(VAR_MSG,NULL,"hub_addr",org_hubip->str,NULL);

      {
            if(getcwd(buf,sizeof(buf))!=NULL)
                  disp_msg(VAR_MSG,NULL,"dl_path",buf,NULL);
      }

      /* if main_sck==-1, we are not connected */
      /* else, we are connected */
      disp_msg(VAR_MSG,NULL,"main_sck","-1",NULL);

      {
            GString *shared_dir;

            shared_dir=get_shared_directory_list();
            disp_msg(VAR_MSG,NULL,"ul_path",shared_dir->str,NULL);
            g_string_free(shared_dir,TRUE);
      }

      sprintf(buf,"%d",((int)cnx_opt)&255);
      disp_msg(VAR_MSG,NULL,"cnx_opt",buf,NULL);

      sprintf(buf,"%d",semctl(bl_semid,UL_SPD_SEMA,GETVAL,v));
      disp_msg(VAR_MSG,NULL,"ubl",buf,NULL);
      sprintf(buf,"%d",semctl(bl_semid,DL_SPD_SEMA,GETVAL,v));
      disp_msg(VAR_MSG,NULL,"dbl",buf,NULL);
      sprintf(buf,"%d",semctl(bl_semid,GATHER_SPD_SEMA,GETVAL,v));
      disp_msg(VAR_MSG,NULL,"gbl",buf,NULL);

      /* display int vars */
      {
            int i;

            for(i=0;tbl_d_flag[i].var_name!=NULL;i++)
            {
                  disp_msg(VAR_MSG,NULL,tbl_d_flag[i].var_name,"|d",(int)(*(tbl_d_flag[i].var_var)),NULL);
            }
      }

      /* number of upload slot */
      disp_msg(VAR_MSG,NULL,"dl_slot","|d",get_number_of_ul_slot(bl_semid),NULL);

      /* display unsigned int vars */
      {
            static struct
            {
                  const char *var_name;
                  unsigned int *var_var;
            } tbl_u_flag[]={
                                                {"recon_delay",&recon_delay},
                                                {"auto_rebuild_delay",&auto_rebuild_delay},
                                                {"max_running_source_per_gdl",&max_running_source_per_gdl},
                                                {"disable_gdl_as_when_enough_running",&disable_gdl_as_when_enough_running},
                                                {"unode_port",&unode_port},
                                                {"sharelist_dl",&sharelist_dl_wo_slot},
                                                {"fake_dcpp_client",&fake_dcpp_client},
                                                {"disp_user",&disp_user},
                                                {NULL,NULL}
                                          };
            int i;

            for(i=0;tbl_u_flag[i].var_name!=NULL;i++)
            {
                  disp_msg(VAR_MSG,NULL,tbl_u_flag[i].var_name,"|u",(unsigned int)(*(tbl_u_flag[i].var_var)),NULL);
            }
      }

      sprintf(buf,"%u",com_port);
      disp_msg(VAR_MSG,NULL,"com_port",buf,NULL);

      /* display socks values */
      disp_msg(VAR_MSG,NULL,"socks_ip",(socks_ip!=NULL)?socks_ip:"",NULL);
      sprintf(buf,"%hu",socks_port);
      disp_msg(VAR_MSG,NULL,"socks_port",buf,NULL);
      disp_msg(VAR_MSG,NULL,"socks_name",(socks_name!=NULL)?socks_name:"",NULL);

      disp_msg(VAR_MSG,NULL,"vshare_dir",(vshare_directory!=NULL)?vshare_directory:"",NULL);

      sprintf(buf,"%u:%u",gdl_as_port_range[0],gdl_as_port_range[1]);
      disp_msg(VAR_MSG,NULL,"gdlasports",buf,NULL);
      UNLOCK_READ(user_info);

      sprintf(buf,"%d,%d,%d,%d",hub_tos,udp_tos,dl_tos,ul_tos);
      disp_msg(VAR_MSG,NULL,"tos",buf,NULL);

      if(fake_dcpp_version==NULL)
            disp_msg(VAR_MSG,NULL,"fake_dcpp_version","",NULL);
      else
            disp_msg(VAR_MSG,NULL,"fake_dcpp_version",fake_dcpp_version->str,NULL);

      disp_msg_ptr_array(VAR_MSG,NULL,"hub_capa",hub_capabilities);
      disp_msg_ptr_array(VAR_MSG,NULL,"dctc_capa",client_capabilities);
      
      disp_met_vars();
      display_cnx_status();
}

typedef struct
{
      const char *cmd;
      size_t cmd_len;
      void (*fnc)(const char *cmd,int sck,char *input, void *xtra_param);
      void *xtra_param;
} KB_CMD;

/********************************************************************/
/* this function process a command line and return its wait_revcon. */
/* no transfer is started. Only /DL and /LS are recognized.         */
/********************************************************************/
/* output: *ptr==NULL on error or the wait_revcon on success */
/*************************************************************/
void indirect_call_process_kbd_com(char *input, WAIT_REVCON **ptr)
{
      static KB_CMD kb_cmd[]= {
                                                            {"/DL ",      sizeof("/DL ")-1,      keyb_do_download, NULL},
                                                            {"/LS ",      sizeof("/LS ")-1,      keyb_do_ls,NULL},
                                                            {NULL,0,NULL},
                                                };
      int i;

      *ptr=NULL;
      i=0;
      while(kb_cmd[i].cmd!=NULL)
      {
            if(!strncmp(input,kb_cmd[i].cmd,kb_cmd[i].cmd_len))
            {
                  kb_cmd[i].fnc(kb_cmd[i].cmd, -1, input, ptr);
                  return;
            }
            i++;
      }
}

/********************/
/* create a new GDL */
/**************************************************************/
/* the cmd syntax is "/GDLNEW number|localfilesize|localfname */
/**************************************************************/
static void keyb_do_gdlnew(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int gdl_id;
      unsigned long int fsize;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",2);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",2+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(arr[2]!=NULL)&&(strlen(arr[2])))
            {
                  gdl_id=strtoul(arr[0],NULL,10);
                  fsize=strtoul(arr[1],NULL,10);
                  do_gdl_new(gdl_id, arr[2],fsize);
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLNEW command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/*****************************************/
/* attach an not used GDL to this client */
/********************************************/
/* the cmd syntax is "/GDLATTACH localfname */
/********************************************/
static void keyb_do_gdlattach(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /GDLATTACH command (1)",NULL);
      }

      do_gdl_attach(t);
}

/***************************************/
/* detach a running GDL of this client */
/***************************************/
/* the cmd syntax is "/GDLDETACH gdlid */
/***************************************/
static void keyb_do_gdldetach(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /GDLDETACH command (1)",NULL);
      }

      do_gdl_detach(strtoul(t,NULL,10));
}


/***********************/
/* add a link in a GDL */
/**********************************************************************/
/* the cmd syntax is "/GDLADD number|nickname|remotefname|remotefsize */
/**********************************************************************/
static void keyb_do_gdladd(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int gdl_id;
      unsigned long int remote_fsize;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 4 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",3);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",3+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1]))&&(arr[2]!=NULL)&&(strlen(arr[2]))&&(arr[3]!=NULL))
            {
                  gdl_id=strtoul(arr[0],NULL,10);
                  remote_fsize=strtoul(arr[3],NULL,10);
                  do_gdl_add(gdl_id, arr[1],arr[2],remote_fsize);
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLADD command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/*************/
/* end a GDL */
/*************************************/
/* the cmd syntax is "/GDLEND number */
/*************************************/
static void keyb_do_gdlend(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int gdl_id;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 4 fields from the array */
      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /GDLEND command (1)",NULL);
      }
      else
      {
            gdl_id=strtoul(t,NULL,10);
            do_gdl_end(gdl_id,0);
      }
}


/**************************/
/* delete a link in a GDL */
/**********************************************************/
/* the cmd syntax is "/GDLDEL number|nickname|remotefname */
/**********************************************************/
static void keyb_do_gdldel(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int gdl_id;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",2);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",2+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1]))&&(arr[2]!=NULL)&&(strlen(arr[2])))
            {
                  gdl_id=strtoul(arr[0],NULL,10);
                  do_gdl_del(gdl_id, arr[1],arr[2]);
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLDEL command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/************************************/
/* add an autoscan pattern to a GDL */
/*****************************************************/
/* the cmd syntax is "/GDLAS+ gdlid|filetype|pattern */
/*****************************************************/
static void keyb_do_gdl_as_plus(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",2);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",2+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1])==1)&&(arr[2]!=NULL)&&(strlen(arr[2])))
            {
                  do_gdl_as_add(    strtoul(arr[0],NULL,10),            /* GDL id */
                                                atoi(arr[1]),                                   /* filetype */
                                                arr[2]);
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLAS+ command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/****************************************************************/
/* the cmd syntax is "/GDLRENAME gdlid|finalname|finaldirectory */
/****************************************************************/
static void keyb_do_gdl_rename(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",2);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",2+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1]))&&(arr[2]!=NULL)&&(strlen(arr[2])))
            {
                  do_gdl_rename(    strtoul(arr[0],NULL,10),/* GDL id */
                                                arr[1],                                   /* filename */
                                                arr[2]);                                  /* dirname */
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLRENAME command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/*****************************************/
/* the cmd syntax is "/GDLNORENAME gdlid */
/*****************************************/
static void keyb_do_gdl_norename(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 3 fields from the array */
      if(strlen(t))
      {
            do_gdl_rename(    strtoul(t,NULL,10),           /* GDL id */
                                                "",                                 /* filename */
                                                "");                                /* dirname */
      }
      else
      {
            disp_msg(ERR_MSG,"","invalid /GDLNORENAME command (1)",NULL);
      }
}

/***************************************************/
/* the cmd syntax is "/GDLSCRIPT gdlid|programname */
/***************************************************/
static void keyb_do_gdl_script(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 2 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",1);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",1+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1])))
            {
                  do_gdl_script(    strtoul(arr[0],NULL,10),/* GDL id */
                                                arr[1]);                                  /* programname */
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLSCRIPT command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/*****************************************/
/* the cmd syntax is "/GDLNOSCRIPT gdlid */
/*****************************************/
static void keyb_do_gdl_noscript(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 1 field from the array */
      if(strlen(t))
      {
            do_gdl_script(    strtoul(t,NULL,10),           /* GDL id */
                                                NULL);                                    /* filename */
      }
      else
      {
            disp_msg(ERR_MSG,"","invalid /GDLNOSCRIPT command (1)",NULL);
      }
}

/****************************************/
/* the cmd syntax is "/GDLCRC gdlid|crc */
/****************************************/
static void keyb_do_gdl_crc(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 2 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",1);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",1+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL)&&(strlen(arr[1])==(2*MD4_DIGEST_LENGTH)))
            {
                  int i;
                  gboolean valid=TRUE;

                  /* check if the second string only contains hexdigits */
                  for(i=0;i<2*MD4_DIGEST_LENGTH;i++)
                  {
                        if(!isxdigit(arr[1][i]))
                        {
                              valid=FALSE;
                              break;
                        }
                  }

                  if(valid)
                  {
                        guint8 md[MD4_DIGEST_LENGTH];

                        id_ascii_to_bin(arr[1],md);
                        do_gdl_set_crc(strtoul(arr[0],NULL,10),/* GDL id */
                                                      md);                                /* programname */
                  }
                  else
                  {
                        disp_msg(ERR_MSG,"","invalid /GDLCRC command: crc is invalid",NULL);
                  }
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLCRC command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/**************************************/
/* the cmd syntax is "/GDLNOCRC gdlid */
/**************************************/
static void keyb_do_gdl_nocrc(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 1 field from the array */
      if(strlen(t))
      {
            do_gdl_set_crc(   strtoul(t,NULL,10),           /* GDL id */
                                                NULL);                                    /* the CRC */
      }
      else
      {
            disp_msg(ERR_MSG,"","invalid /GDLCRC command (1)",NULL);
      }
}

/***************************************/
/* remove an autoscan pattern to a GDL */
/******************************************/
/* the cmd syntax is "/GDLAS+ gdlid|gasID */
/******************************************/
static void keyb_do_gdl_as_minus(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned int gdl_id;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 2 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",1);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",1+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL))
            {
                  gdl_id=strtoul(arr[0],NULL,10);
                  do_gdl_as_del(    strtoul(arr[0],NULL,10),
                                                strtoul(arr[1],NULL,10));
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /GDLAS+ command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}


/*******************************/
/* wrapper to GDL?LST function */
/********************************/
/* the cmd syntax is "/GDL?LST" */
/********************************/
static void keyb_do_gdlxlst(const char *cmd, int sck, char *input, void *xtra_param)
{
      void (*fnc)(int sck);

      fnc=xtra_param;

      (*fnc)(sck);
}

/*******************************/
/* wrapper to GDL?LST function */
/********************************/
/* the cmd syntax is "/SLOWGDL?LST" */
/*******************************************************************************/
/* this function works nearly exactly like the previous one                    */
/* the only difference is it cannot be called more than 1 time every 8 seconds */
/*******************************************************************************/
static void keyb_do_slow_gdlxlst(const char *cmd, int sck, char *input, void *xtra_param)
{
      void (*fnc)(int sck);
      char *dummy1;
      int dummy1_len;

      fnc=xtra_param;

      if(get_tos_entry(LMTCMD_TOSKEY,cmd,strlen(cmd),0,&dummy1,&dummy1_len)==0)
      {
            add_tos_entry(LMTCMD_TOSKEY,4,cmd,strlen(cmd),NULL,0);
            (*fnc)(sck);
      }
      else
      {
            if(dummy1!=NULL)
                  free(dummy1);
      }
}

/****************************/
/* set a new .met directory */
/*****************************************/
/* the cmd syntax is "/GDLMETDIR dirname */
/*****************************************/
static void keyb_do_gdlmetdir(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      set_met_dir(t);
}

/*******************************************************/
/* set the delay between 2 scans of the .met directory */
/*******************************************************/
/* the cmd syntax is "/GDLMETPOLL duration */
/*******************************************/
static void keyb_do_gdlmetpoll(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      set_met_scan_interval(atoi(t));
}


/**********************************/
/* add an UADDR entry (nick;host) */
/***********************************************************/
/* the cmd syntax is "/UADDRUADD nickname|hostip:hostport" */
/***********************************************************/
static void keyb_do_uaddruadd(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      gchar **arr;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      /* extract 2 fields from the array */
#ifndef WITH_GLIB2
      arr=g_strsplit(t,"|",1);            /* GLIB2 fixed */
#else
      arr=g_strsplit(t,"|",1+1);    /* GLIB2 fix */
#endif
      if(arr!=NULL)
      {
            if((arr[0]!=NULL)&&(arr[1]!=NULL))
            {
                  add_uaddr_entry(arr[0],arr[1]);
            }
            else
            {
                  disp_msg(ERR_MSG,"","invalid /UADDRUADD command (1)",NULL);
            }
      }

      if(arr!=NULL)
            g_strfreev(arr);
}

/******************************************************/
/* add/del an UADDR entry by nickname or host address */
/*************************************************************************************************************/
/* the cmd syntax is "/UADDRUDEL nickname" or "/UADDRIPDEL hostip:hostport" or "/UADDRIPADD hostip:hostport" */
/*************************************************************************************************************/
static void keyb_do_uaddrgeneric(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      int (*fnc)(char *str)=xtra_param;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /UADDR* command (1)",NULL);
      }
      else
      {
            (*fnc)(t);
      }
}

/***************/
/* kick a user */
/**************************************/
/* the cmd syntax is "/KICK nickname" */
/**************************************/
static void keyb_do_kick(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /KICK command (1)",NULL);
      }
      else
      {
            send_dc_line(sck,"$Kick",t,NULL);
      }
}

/**********************/
/* add local ban user */
/***************************************/
/* the cmd syntax is "/ULBAN nickname" */
/***************************************/
static void keyb_do_ulban(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /ULBAN command (1)",NULL);
      }
      else
      {
            /* FIXME: command not written */
      }
}

/*************************/
/* remove local ban user */
/*****************************************/
/* the cmd syntax is "/ULUNBAN nickname" */
/*****************************************/
static void keyb_do_ulunban(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /ULUNBAN command (1)",NULL);
      }
      else
      {
            /* FIXME: command not written */
      }
}


/***********************************/
/* list all the known UADDR couple */
/***********************************/
/* the cmd syntax is "/UADDRLST" */
/*********************************/
static void keyb_do_uaddrlst(const char *cmd, int sck, char *input, void *xtra_param)
{  
      list_uaddr_content();
}

/****************************************/
/* this function changes the unode port */
/*****************************************/
/* the cmd syntax is "/UNODEPORT number" */
/*****************************************/
static void keyb_do_unodeport(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      unsigned short slot;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /UNODEPORT command (1)",NULL);
            return;
      }

      /* modify the number of slot */
      slot=strtoul(t,NULL,10);
      if(slot==0)
      {
            disp_msg(ERR_MSG,"","invalid /UNODEPORT command (2), must use a port number between 1 and 65535",NULL);
            return;
      }

      wanted_unode_port=slot;

      /* the UNODE is perhaps not on this client, also notify this change to all other clients */
      if(!strncmp(input,"/UNODEPORT ",strlen("/UNODEPORT ")))
      {
            input[1]='u';
            send_dc_line_to_dctc_link_direct(NULL,input);
            input[1]='U';
      }
}

/*************************************************************/
/* locate the position of a user and sends it to all clients */
/*************************************************************/
static void locate_a_user_and_send_position(char *nickname)
{
      if(user_in_list(hub_user_list,nickname))
      {
            GString *str;
            GString *ip;

            ip=get_uaddr_dl_addr_by_name(nickname);

            disp_msg_full(LOCATE_USER,&str,"",nickname,org_hubip->str,"", ((ip==NULL)?"":ip->str) ,NULL);

            if(ip!=NULL)
                  g_string_free(ip,TRUE);

            if(str!=NULL)
            {     /* we also need to relay this result to other local clients */
                  str=g_string_truncate(str,str->len-1);          /* remove the trailing \n */
                  str=g_string_prepend_c(str,'[');
                  send_dc_line_to_dctc_link_direct(NULL,str->str);
                  g_string_free(str,TRUE);
            }
      }
}

/******************************************************/
/* this function tries to find on which hub a user is */
/******************************************************/
/* the cmd syntax is "/LOCATEUSER nickname" */
/********************************************/
static void keyb_do_locateuser(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /LOCATEUSER command (1)",NULL);
            return;
      }

      locate_a_user_and_send_position(t);

      /* the user is perhaps not on this client, also make this query to all other clients */
      if(!strncmp(input,"/LOCATEUSER ",strlen("/LOCATEUSER ")))
      {
            input[1]='l';
            send_dc_line_to_dctc_link_direct(t,input);
            input[1]='L';
      }
}


/****************************************************************/
/* this function wakes up GDL sources having the given nickname */
/****************************************************************/
/* the cmd syntax is "/WAKEUGL nickname" */
/*****************************************/
static void keyb_do_wakeugdl(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;
      char *nick;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"","invalid /WAKEUGDL command (1)",NULL);
            return;
      }

      nick=t;
      t=strchr(nick,'|');
      if(t!=NULL)
            *t='\0';

      if(!tos_entry_exists(WAKEUGDL_TOSKEY,nick,strlen(nick),0))
      {
            add_tos_entry_v1_uniq(WAKEUGDL_TOSKEY,min_gdl_wake_up_delay,nick,strlen(nick),NULL,0);
            gdl_wake_up_sources(nick,0);
      }
}

/*************************************************/
/* this function changes the client capabilities */
/*************************************************/
/* the cmd syntax is "/CAPAB val1[$val2...]" */
/*********************************************/
static void keyb_do_set_capabilities(const char *cmd, int sck, char *input, void *xtra_param)
{
      char *t;

      t=input+strlen(cmd);
      SKIP_SPACE(t)

      wipe_GPtrArray_of_char_string(&client_capabilities);
      if(*t!='\0')
      {
            gchar **arr;
            int i;

            client_capabilities=g_ptr_array_new();

            arr=g_strsplit(t,"$",0);
      
            i=0;
            while(arr[i]!=NULL)
            {
                  g_ptr_array_add(client_capabilities,strdup(arr[i]));
                  i++;
            }
            g_strfreev(arr);
      }

      send_client2hub_capabilities();
}

/***************************************************/
/* this function queries the hub for a partial CRC */
/*******************************************************/
/* the cmd syntax is "/MD4GET|global_crc|file_length|" */
/*******************************************************/
static void keyb_do_md4get(const char *cmd, int sck, char *input, void *xtra_param)
{
      gchar **fields;
      int i;

      if(!find_a_char_string_in_GPtrArray(&hub_capabilities,"MD4x"))    /* check if the hub has the MD4x capability */
            return;

      /* create a copy of the input string. We need it if we have to requeue the download */
#ifndef WITH_GLIB2
      fields=g_strsplit(input,"|",3);           /* GLIB2 fixed */
#else
      fields=g_strsplit(input,"|",3+1);   /* GLIB2 fix */
#endif
      if(strlen(fields[1])!=(2*MD4_DIGEST_LENGTH))
      {
            disp_msg(ERR_MSG,"","invalid /MD4GET command (1)",NULL);
            goto abrt;
      }

      for(i=0;i<(2*MD4_DIGEST_LENGTH);i++)
      {
            if(!isxdigit(fields[1][i]))
            {
                  disp_msg(ERR_MSG,"","invalid /MD4GET command (2)",NULL);
                  goto abrt;
            }
      }

      LOCK_READ(user_info);
      send_dc_line(sck,"$MD4Get0",nickname,fields[1],fields[2],NULL);
      UNLOCK_READ(user_info);

      /* the MD4GET is perhaps not on this client, also notify this change to all other clients */
      if(!strncmp(input,"/MD4GET|",strlen("/MD4GET|")))
      {
            input[1]='m';
            send_dc_line_to_dctc_link_direct(NULL,input);
            input[1]='M';
      }

      abrt:
      if(fields)
            g_strfreev(fields);
}

/************************************************************************/
/* this function sends a received partial CRC to all other local client */
/****************************************************************************/
/* the cmd syntax is "/MD4SET|global_crc|file_length|partial_crc|filename|" */
/****************************************************************************/
static void keyb_do_md4set(const char *cmd, int sck, char *input, void *xtra_param)
{
      gchar **fields;
      int i;
      unsigned long fsize;
      int nb_seg;

      if(!find_a_char_string_in_GPtrArray(&client_capabilities,"MD4x")) /* check if the hub has the MD4x capability */
            return;

      /* create a copy of the input string. We need it if we have to requeue the download */
#ifndef WITH_GLIB2
      fields=g_strsplit(input,"|",5);           /* GLIB2 fixed */
#else
      fields=g_strsplit(input,"|",5+1);   /* GLIB2 fix */
#endif

      /* check the main crc */
      if(strlen(fields[1])!=(2*MD4_DIGEST_LENGTH))
      {
            disp_msg(ERR_MSG,"","invalid /MD4GET command (1)",NULL);
            goto abrt;
      }

      for(i=0;i<(2*MD4_DIGEST_LENGTH);i++)
      {
            if(!isxdigit(fields[1][i]))
            {
                  disp_msg(ERR_MSG,"","invalid /MD4GET command (2)",NULL);
                  goto abrt;
            }
      }

      sscanf(fields[2],"%lu",&fsize);

      /* check the partial crc */
      nb_seg=(fsize+PARTSIZE-1)/PARTSIZE;
      if(strlen(fields[3])!=(nb_seg*2*MD4_DIGEST_LENGTH))
      {
            disp_msg(ERR_MSG,"","invalid /MD4GET command (3)",NULL);
            goto abrt;
      }

      for(i=0;i<(nb_seg*2*MD4_DIGEST_LENGTH);i++)
      {
            if(!isxdigit(fields[3][i]))
            {
                  disp_msg(ERR_MSG,"","invalid /MD4GET command (4)",NULL);
                  goto abrt;
            }
      }

      /* check if the partial crc match the global crc */
      {
            unsigned char gmd[MD4_DIGEST_LENGTH];

            unsigned char md[MD4_DIGEST_LENGTH];
            guint8 *tmp_md;

            /* convert the global CRC into a binary value */
            id_ascii_to_bin(fields[1],gmd);

            tmp_md=malloc(nb_seg*MD4_DIGEST_LENGTH);
            if(tmp_md==NULL)
            {
                  disp_msg(ERR_MSG,"","out of memory",NULL);
                  goto abrt;
            }
            
            /* convert the partial CRC into a binary value */
            for(i=0;i<nb_seg;i++)
            {
                  id_ascii_to_bin(fields[3]+i*2*MD4_DIGEST_LENGTH,tmp_md+i*MD4_DIGEST_LENGTH);
            }

            MD4_ALGO(tmp_md,nb_seg*MD4_DIGEST_LENGTH,md);
            
            if(memcmp(md,gmd,MD4_DIGEST_LENGTH))
            {
                  free(tmp_md);
                  disp_msg(ERR_MSG,"","invalid /MD4GET command (4): partial and global CRC does not match",NULL);
                  goto abrt;
            }

            incoming_partial_md(gmd,fsize,tmp_md,nb_seg*MD4_DIGEST_LENGTH);

            free(tmp_md);
      }

      /* the MD4SET is perhaps not on this client, also notify this change to all other clients */
      if(!strncmp(input,"/MD4SET|",strlen("/MD4SET|")))
      {
            input[1]='m';
            send_dc_line_to_dctc_link_direct(NULL,input);
            input[1]='M';
      }

      abrt:
      if(fields)
            g_strfreev(fields);
}

/**************************************************************/
/* this function process keyboard input and do what it should */
/**************************************************************/
/*************************************************************************************/
/* this function is called each time the client receives a full line from the server */
/*****************************************************************************************/
/* sck is provided because the function to process may requires and access to the socket */
/*****************************************************************************************/
/***********************/
static void process_keyboard_command(int sck, char *input)
{
      static KB_CMD kb_cmd[]= {
                                                            {"/DL ",      sizeof("/DL ")-1,      keyb_do_download, NULL},
                                                            {"/XDL|",     sizeof("/XDL|")-1,     keyb_do_xdownload, NULL},
                                                            {"/SRCH ",    sizeof("/SRCH ")-1,    keyb_do_search,NULL},
                                                            {"/CSRCH ",   sizeof("/CSRCH ")-1,   keyb_do_csearch,NULL},
                                                            {"/MSRCH ",   sizeof("/MSRCH ")-1,   keyb_do_msearch,NULL},
                                                            {"/CHAT ",    sizeof("/CHAT ")-1,    keyb_do_chat,NULL},
                                                            {"/PRIV ",    sizeof("/PRIV ")-1,    keyb_do_priv_chat,NULL},
                                                            {"/SLOT ",    sizeof("/SLOT ")-1,    keyb_do_slot,NULL},
                                                            {"/PORT ",    sizeof("/PORT ")-1,    keyb_do_port,NULL},
                                                            {"/LPATH ",   sizeof("/LPATH ")-1,   keyb_do_lpath,NULL},
                                                            {"/IP ",      sizeof("/IP ")-1,      keyb_do_ip,NULL},
                                                            {"/ACTIVE",   sizeof("/ACTIVE")-1,   keyb_do_active,NULL},/* no space after the E, there is no parameter */
                                                            {"/PASSIVE",  sizeof("/PASSIVE")-1,  keyb_do_passive,NULL},/* no space after the E, there is no parameter */
                                                            {"/XFER",     sizeof("/XFER")-1,     keyb_do_xfer,NULL},    /* no space after the R, there is no parameter */
                                                            {"/XFERKB",   sizeof("/XFERKB")-1,   keyb_do_xfer,NULL},    /* no space after the B, there is no parameter */
                                                            {"/ULIST_FORCE",sizeof("/ULIST_FORCE")-1,keyb_do_ulist,NULL},/* no space after the E, there is no parameter */
                                                                                                                                                                              /* this command MUST be before ULIST */
                                                            {"/ULIST",    sizeof("/ULIST")-1,    keyb_do_ulist,NULL},/* no space after the T, there is no parameter */
                                                            {"/UINFO ",   sizeof("/UINFO ")-1,   keyb_do_uinfo,NULL},
                                                            {"/KILL ",    sizeof("/KILL ")-1,    keyb_do_kill,NULL},
                                                            {"/KILLKB ",  sizeof("/KILLKB ")-1,  keyb_do_killkb,NULL},
                                                            {"/KILLKBN ", sizeof("/KILLKBN ")-1, keyb_do_killkbn,NULL},
                                                            {"/QUIT",     sizeof("/QUIT")-1,     keyb_do_quit,NULL},/* no space after the E, there is no parameter */
                                                            {"/FORCEQUIT",sizeof("/FORCEQUIT")-1,keyb_do_quit,NULL},/* no space after the E, there is no parameter */
                                                            {"/DLON",     sizeof("/DLON")-1,     keyb_do_dlon,NULL},/* no space after the N, there is no parameter */
                                                            {"/DLOFF",    sizeof("/DLOFF")-1,    keyb_do_dloff,NULL},/* no space after the F, there is no parameter */
                                                            {"/DEBUG",    sizeof("/DEBUG")-1,    keyb_do_debug,NULL},/* no space after the E, there is no parameter */
                                                            {"/LOG ",     sizeof("/LOG ")-1,     keyb_do_log,NULL},
                                                            {"/NOLOG",    sizeof("/NOLOG")-1,    keyb_do_nolog,NULL},/* no space after the G, there is no parameter */
                                                            {"/ERRLOG ",  sizeof("/ERRLOG ")-1,  keyb_do_errlog,NULL},
                                                            {"/NOERRLOG", sizeof("/NOERRLOG")-1, keyb_do_noerrlog,NULL},/* no space after the G, there is no parameter */

                                                            {"/RECOND ",  sizeof("/RECOND ")-1,  keyb_do_recond,NULL},  /* must be before /RECON */

                                                            {"/HUBNAME",  sizeof("/HUBNAME")-1,  keyb_do_hubname,NULL},/* no space after the E, there is no parameter */
                                                            {"/VARS",     sizeof("/VARS")-1,     keyb_do_vars,NULL},/* no space after the S, there is no parameter */
                                                            {"/LS ",      sizeof("/LS ")-1,      keyb_do_ls,NULL},
                                                            {"/UBL ",     sizeof("/UBL ")-1,     keyb_do_xbl,GINT_TO_POINTER(UL_SPD_SEMA)},
                                                            {"/DBL ",     sizeof("/DBL ")-1,     keyb_do_xbl,GINT_TO_POINTER(DL_SPD_SEMA)},
                                                            {"/GBL ",     sizeof("/GBL ")-1,     keyb_do_xbl,GINT_TO_POINTER(GATHER_SPD_SEMA)},

                                                            {"/DONE",               sizeof("/DONE")-1,            keyb_do_set_int_flag,&when_done},
                                                            {"/UNDONE",             sizeof("/UNDONE")-1,          keyb_do_unset_int_flag,&when_done},
                                                            {"/TOS ",               sizeof("/TOS ")-1,            keyb_do_tos,NULL},
                                                            {"/DDL",                sizeof("/DDL")-1,             keyb_do_set_int_flag,&with_ddl},
                                                            {"/NODDL",              sizeof("/NODDL")-1,           keyb_do_unset_int_flag,&with_ddl},
                                                            {"/GBANIP",             sizeof("/GBANIP")-1,          keyb_do_set_int_flag,&grab_ban_ip},
                                                            {"/NOGBANIP",           sizeof("/NOGBANIP")-1,        keyb_do_unset_int_flag,&grab_ban_ip},
                                                            {"/MAXRUNGDLSRC ",sizeof("/MAXRUNGDLSRC ")-1,keyb_do_set_unsigned_int,&max_running_source_per_gdl},
                                                            {"/GDLASOFFAFT ", sizeof("/GDLASOFFAFT ")-1, keyb_do_set_unsigned_int,&disable_gdl_as_when_enough_running},
                                                            {"/CAPAB ",             sizeof("/CAPAB ")-1,                keyb_do_set_capabilities,NULL},
                                                            
                                                            /* group download functions */
                                                            {"/GDLNEW ",            sizeof("/GDLNEW ")-1,         keyb_do_gdlnew,NULL},
                                                            {"/GDLATTACH ",   sizeof("/GDLATTACH ")-1,      keyb_do_gdlattach,NULL},
                                                            {"/GDLADD ",            sizeof("/GDLADD ")-1,         keyb_do_gdladd,NULL},
                                                            {"/GDLDEL ",            sizeof("/GDLDEL ")-1,         keyb_do_gdldel,NULL},
                                                            {"/GDLEND ",            sizeof("/GDLEND ")-1,         keyb_do_gdlend,NULL},
                                                            {"/GDLQLST",            sizeof("/GDLQLST")-1,         keyb_do_gdlxlst,do_gdl_qlst},
                                                            {"/GDLLST",             sizeof("/GDLLST")-1,          keyb_do_gdlxlst,do_gdl_lst},
                                                            {"/SLOWGDLLST",   sizeof("/SLOWGDLLST")-1,   keyb_do_slow_gdlxlst,do_gdl_lst},
                                                            {"/GDLAS+ ",            sizeof("/GDLAS+ ")-1,         keyb_do_gdl_as_plus,NULL},
                                                            {"/GDLAS- ",            sizeof("/GDLAS- ")-1,         keyb_do_gdl_as_minus,NULL},
                                                            {"/GDLRENAME ",   sizeof("/GDLRENAME ")-1,      keyb_do_gdl_rename,NULL},
                                                            {"/GDLNORENAME ", sizeof("/GDLNORENAME ")-1,    keyb_do_gdl_norename,NULL},
                                                            {"/GDLSCRIPT ",   sizeof("/GDLSCRIPT ")-1,      keyb_do_gdl_script,NULL},
                                                            {"/GDLNOSCRIPT ", sizeof("/GDLNOSCRIPT ")-1,    keyb_do_gdl_noscript,NULL},
                                                            {"/GDLDETACH ",   sizeof("/GDLDETACH ")-1,      keyb_do_gdldetach,NULL},
                                                            {"/GDLASPORTS ",  sizeof("/GDLASPORTS ")-1,  keyb_do_gdl_as_ports,NULL},
                                                            {"/GDLCRC ",            sizeof("/GDLCRC ")-1,         keyb_do_gdl_crc,NULL},
                                                            {"/GDLNOCRC ",          sizeof("/GDLNOCRC ")-1,       keyb_do_gdl_nocrc,NULL},
                                                            {"/LAZYKC",             sizeof("/LAZYKC")-1,          keyb_do_set_int_flag,&with_lazy_key_check},
                                                            {"/NOLAZYKC",           sizeof("/NOLAZYKC")-1,        keyb_do_unset_int_flag,&with_lazy_key_check},
                                                            {"/MAXUDL ",            sizeof("/MAXUDL ")-1,         keyb_do_set_int_value,&max_dl_per_user},
#if 0
                                                            {"/GDLMETFILE ",  sizeof("/GDLMETFILE ")-1,     keyb_do_gdlmetfile,NULL},
#endif
                                                            {"/GDLMETDIR ",   sizeof("/GDLMETDIR ")-1,      keyb_do_gdlmetdir,NULL},
                                                            {"/GDLMETPOLL ",  sizeof("/GDLMETPOLL ")-1,     keyb_do_gdlmetpoll,NULL},

                                                            /* uaddr functions */
                                                            {"/UADDRUADD "    ,     sizeof("/UADDRUADD ")-1,      keyb_do_uaddruadd,NULL},
                                                            {"/UADDRUDEL "    ,     sizeof("/UADDRUDEL ")-1,      keyb_do_uaddrgeneric,delete_uaddr_entry_by_name},
                                                            {"/UADDRIPDEL ",  sizeof("/UADDRIPDEL ")-1,     keyb_do_uaddrgeneric,delete_uaddr_entry_by_addr},
                                                            {"/UADDRIPADD ",  sizeof("/UADDRIPADD ")-1,     keyb_do_uaddrgeneric,add_uaddr_entry_addr_dl_only},
                                                            {"/UADDRLST"      ,     sizeof("/UADDRLST")-1,        keyb_do_uaddrlst,NULL}, /* no space after the T, there is no parameter */

                                                            /* user local "ban" functions */
                                                            {"/ULBAN "        ,     sizeof("/ULBAN ")-1,                keyb_do_ulban,NULL},
                                                            {"/ULUNBAN "      ,     sizeof("/ULUNBAN ")-1,        keyb_do_ulunban,NULL},
                                                            {"/KICK "         ,  sizeof("/KICK ")-1,              keyb_do_kick,NULL},
                                                
                                                            {"/UNODEPORT "    ,     sizeof("/UNODEPORT ")-1,      keyb_do_unodeport,NULL},
                                                            {"/uNODEPORT "    ,     sizeof("/uNODEPORT ")-1,      keyb_do_unodeport,NULL},      /* version not relaying message on dctc-link */

                                                            {"/DFLAG "        ,     sizeof("/DFLAG ")-1,                keyb_do_dflag_set,NULL},
                                                            {"/WAKEUGDL "     ,     sizeof("/WAKEUGDL ")-1,       keyb_do_wakeugdl,NULL},

                                                            {"/LOCATEUSER ",  sizeof("/LOCATEUSER ")-1,     keyb_do_locateuser,NULL},
                                                            {"/lOCATEUSER ",  sizeof("/lOCATEUSER ")-1,     keyb_do_locateuser,NULL},     /* version not relaying message on dctc-link */

                                                            {"/MD4GET|",            sizeof("/MD4GET|")-1,         keyb_do_md4get,NULL},
                                                            {"/mD4GET|",            sizeof("/mD4GET|")-1,         keyb_do_md4get,NULL},         /* version not relaying message on dctc-link */

                                                            {"/MD4SET|",            sizeof("/MD4SET|")-1,         keyb_do_md4set,NULL},
                                                            {"/mD4SET|",            sizeof("/mD4SET|")-1,         keyb_do_md4set,NULL},         /* version not relaying message on dctc-link */

                                                            {"/HELLODCTC",  sizeof("/HELLODCTC")-1,      keyb_do_hellodctc,NULL},   /* no space after the C, there is no parameter */

                                                            {NULL,0,NULL},
                                                };

      int i;

      if(strlen(input)<2)           /* nothing to process */
            return;                 /* end */

      i=0;
      while(kb_cmd[i].cmd!=NULL)
      {
            if(!strncmp(input,kb_cmd[i].cmd,kb_cmd[i].cmd_len))
            {
                  kb_cmd[i].fnc(kb_cmd[i].cmd, sck,input, kb_cmd[i].xtra_param);
                  return;
            }
            i++;
      }

      printf("unknown command: %s\n",input);
}

/**********************************************************/
/* this function manages everything enter on the keyboard */
/*************************************************************************/
/* input: sck= network socket to use (should be main_sck)                */
/*        sim_input= if sim_input==NULL, the command is taken from stdin */
/*                   else, the given command is used                     */
/*************************************************************************/
void keyboard_input(int sck, char *sim_input)
{
      char buf[51200];
      char *t;

      if(sim_input==NULL)
      {
            if(fgets(buf,sizeof(buf),stdin)==NULL)
            {
                  keyb_fd=-1;
                  return;
            }
      }
      else
            strncpy_max(buf,sim_input,sizeof(buf));

      if(buf[0]=='/')
            t=strchr(buf,'\n');           /* break a / cmd at the first \n */
      else
            t=strrchr(buf,'\n');          /* all other commands, break at the last \n */
      if(t!=NULL)
            *t='\0';

      if(buf[0]=='$')
      {     /* a DC command has been received */

            if(!strncmp("$Search ",buf,strlen("$Search ")))
            {
                  /* check if the delay between searchs is running */
                  if(!tos_entry_exists(LMTSRCH_TOSKEY,"delay",5,0))
                  {
                        /* set search query limiter timeout */
                        if(min_delay_between_search)
                              add_tos_entry_v1_uniq(LMTSRCH_TOSKEY,min_delay_between_search,"delay",5,NULL,0);
                        goto send_dc_cmd;
                  }
            }
            else
            {
                  send_dc_cmd:
                  if(send(sck,buf,strlen(buf),MSG_NOSIGNAL)!=strlen(buf))
                  {
                        disp_msg(HUB_DISCONNECT,"","Hub has closed its connection.",NULL);
                        hub_disconnect(DO_RECONNECT);
                  }
            }
      }
      else if(buf[0]=='~')
      {     /* a conditionnal DC command has been received */
            char *t1;

            printf("cmd: '%s'\n",buf);

            /* syntax: ~nickname|$cmd */
            /* check if the given nickname is on the hub. If yes, the $cmd is sent as usual */

            t1=strchr(buf+1,'|');
            if(t1!=NULL)
            {
                  *t1++='\0';
                  if(strlen(t1))
                  {
                        if(user_in_list(hub_user_list,buf+1))
                        {
                              printf("~cmd (%s): '%s'\n",buf+1,t1);
                              if(send(sck,t1,strlen(t1),MSG_NOSIGNAL)!=strlen(t1))
                              {
                                    disp_msg(HUB_DISCONNECT,"","Hub has closed its connection.",NULL);
                                    hub_disconnect(DO_RECONNECT);
                              }
                        }
                  }
            }
      }
      else if(buf[0]=='*')
      {     /* a DC command result to relay has been received */
            /* NOTE: not all DC command result can be forward to other DCTC clients */
            /* only the one not using send_dc_line can */
            /* to avoid infinite loop, the first $ is converted in * in the DC message */
            /* thus the called function can know if it is a message redirection or a direct message */
            int ln;
            GString *icmd;

            printf("cmd: '%s'\n",buf);

            ln=strlen(buf);
            if(ln>1)
            {
                  if(buf[ln-1]=='\n')
                        buf[ln-1]='\0';

                  if(buf[1]=='$')
                        buf[1]='*';

                  icmd=g_string_new(buf+1);
                  process_incoming_dc_data(sck,icmd);
                  g_string_free(icmd,TRUE);
            }
      }
      else if(buf[0]=='/')
      {
            process_keyboard_command(sck,buf);
      }
      else if(buf[0]=='[')
      {
            disp_msg(DISPLAY_RELAY,NULL,buf+1);
      }
}

/*****************************************************/
/* this function is called for each --precmd command */
/*****************************************************/
void process_precmd_command(char *input)
{
      static KB_CMD kb_cmd[]= {
                                                            {"/SLOT ",    sizeof("/SLOT ")-1,    keyb_do_slot,NULL},                                                                                        /* = */
                                                            {"/LPATH ",   sizeof("/LPATH ")-1,   keyb_do_lpath,NULL},                                                                                 /* = */
                                                            {"/IP ",      sizeof("/IP ")-1,      keyb_do_ip,NULL},                                                                                          /* = */
                                                            {"/DLON",     sizeof("/DLON")-1,     keyb_do_dlon,NULL},/* no space after the N, there is no parameter */         /* = */
                                                            {"/DLOFF",    sizeof("/DLOFF")-1,    keyb_do_dloff,NULL},/* no space after the F, there is no parameter */        /* = */
                                                            {"/DEBUG",    sizeof("/DEBUG")-1,    keyb_do_debug,NULL},/* no space after the E, there is no parameter */        /* = */
                                                            {"/LOG ",     sizeof("/LOG ")-1,     keyb_do_log,NULL},                                                                                                                 /* = */
                                                            {"/NOLOG",    sizeof("/NOLOG")-1,    keyb_do_nolog,NULL},/* no space after the G, there is no parameter */        /* = */
                                                            {"/ERRLOG ",  sizeof("/ERRLOG ")-1,  keyb_do_errlog,NULL},                                                                                                        /* = */
                                                            {"/NOERRLOG", sizeof("/NOERRLOG")-1, keyb_do_noerrlog,NULL},/* no space after the G, there is no parameter */     /* = */
                                                            {"/RECOND ",  sizeof("/RECOND ")-1,  keyb_do_recond,NULL},
                                                            {"/DONE",     sizeof("/DONE")-1,     keyb_do_set_int_flag,&when_done},
                                                            {"/UNDONE",   sizeof("/UNDONE")-1,   keyb_do_unset_int_flag,&when_done},
                                                            {"/TOS ",     sizeof("/TOS ")-1,     keyb_do_tos,NULL},
                                                            {"/DDL",      sizeof("/DDL")-1,      keyb_do_set_int_flag,&with_ddl},
                                                            {"/NODDL",    sizeof("/NODDL")-1,    keyb_do_unset_int_flag,&with_ddl},
                                                            {"/GBANIP",   sizeof("/GBANIP")-1,   keyb_do_set_int_flag,&grab_ban_ip},
                                                            {"/NOGBANIP", sizeof("/NOGBANIP")-1, keyb_do_unset_int_flag,&grab_ban_ip},
                                                            {"/MAXRUNGDLSRC ",sizeof("/MAXRUNGDLSRC ")-1, keyb_do_set_unsigned_int,&max_running_source_per_gdl},
                                                            {"/GDLASOFFAFT ", sizeof("/GDLASOFFAFT ")-1,  keyb_do_set_unsigned_int,&disable_gdl_as_when_enough_running},
                                                            {"/LAZYKC",    sizeof("/LAZYKC")-1,    keyb_do_set_int_flag,&with_lazy_key_check},
                                                            {"/NOLAZYKC",  sizeof("/NOLAZYKC")-1,  keyb_do_unset_int_flag,&with_lazy_key_check},
                                                            {"/UNODEPORT ",sizeof("/UNODEPORT ")-1,keyb_do_unodeport,NULL},
                                                            {"/DFLAG "    ,sizeof("/DFLAG ")-1,    keyb_do_dflag_set,NULL},
                                                            {"/MAXUDL "   ,sizeof("/MAXUDL ")-1,   keyb_do_set_int_value,&max_dl_per_user},
                                                            {"/GDLASPORTS ",sizeof("/GDLASPORTS ")-1,keyb_do_gdl_as_ports,NULL},
                                                            {"/CAPAB ",    sizeof("/CAPAB ")-1,    keyb_do_set_capabilities,NULL},
                                                            {"/GDLMETDIR ",   sizeof("/GDLMETDIR ")-1,      keyb_do_gdlmetdir,NULL},
                                                            {"/GDLMETPOLL ",  sizeof("/GDLMETPOLL ")-1,     keyb_do_gdlmetpoll,NULL},
      
                                                            {NULL,0,NULL},
                                                };

      int i;

      if((input==NULL)||(strlen(input)<2))            /* nothing to process */
            return;                 /* end */

      i=0;
      while(kb_cmd[i].cmd!=NULL)
      {
            if(!strncmp(input,kb_cmd[i].cmd,kb_cmd[i].cmd_len))
            {
                  kb_cmd[i].fnc(kb_cmd[i].cmd, -1,input, kb_cmd[i].xtra_param);
                  return;
            }
            i++;
      }

      printf("unknown command: %s\n",input);
}



Generated by  Doxygen 1.6.0   Back to index