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

dc_xfer_common.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * dc_xfer_common.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: dc_xfer_common.c,v 1.3 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 <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#include <glib.h>

#include "dc_xfer_common.h"
#include "hl_locks.h"
#include "var.h"
#include "action.h"
#include "display.h"
#include "sema_common.h"
#include "mydb.h"
#include "dc_com.h"
#include "macro.h"
#include "user_manage.h"
#include "gdl.h"
#include "ls_cache.h"
#include "gts.h"
#include "he3.h"
#include "uaddr.h"
#include "network.h"
#include "key.h"
#include "userinfo.h"
#include "misc.h"
#include "main.h"

int hub_logged=0; /* set to 1 when dctc is logged on the hub */

/********************************************************/
/* when someone connects on com port, send our nickname */
/********************************************************/
/* output: 0=ok, !=0=fail */
/**************************/
int send_nick(int sck)
{
      GString *str;
      int a;

      str=g_string_new(NULL);
      LOCK_READ(user_info);
      g_string_sprintf(str,"$MyNick %s|",nickname);
      UNLOCK_READ(user_info);

#if 0
      a=write(sck,str->str,str->len);                 /* @@@ replaced by send */
      a=(a!=str->len);
#else
      a=( (send(sck,str->str,str->len,MSG_NOSIGNAL)) != str->len );
#endif

      g_string_free(str,TRUE);
      return a;                     /* 1=fail, 0=ok */
}

/******************************************************/
/* take the given $Direction string and decode fields */
/**********************************************************/
/* output: MY_DIR_ERROR, MY_DIR_DOWNLOAD or MY_DIR_UPLOAD */
/*         if no error occurs, *level contains the level  */
/**********************************************************/
MY_DIR decode_direction(GString *input, unsigned int *level)
{
      char *t;

      if(input==NULL)
            return MY_DIR_ERROR;

      if(strncmp(input->str,"$Direction ",sizeof("$Direction ")-1))
            return MY_DIR_ERROR;

      t=input->str+ sizeof("$Direction ")-1;

      if(!strncmp(t,"Download",sizeof("Download")-1))
      {
            *level=strtoul(t+sizeof("Download"),NULL,10);
            return MY_DIR_DOWNLOAD;
      }
      else if(!strncmp(t,"Upload",sizeof("Upload")-1))
      {
            *level=strtoul(t+sizeof("Upload"),NULL,10);
            return MY_DIR_UPLOAD;
      }
      else
            return MY_DIR_ERROR;
}

/***************************************************************************/
/* create a lock inside the 'created_lock' array and send it on the socket */
/***************************************************************************/
int send_a_lock(int sck,char *created_lock)
{
      int i;
      int ln;
      int j;

      ln=50+rand()%50;

      for(i=0;i<ln;i++)
      {
            created_lock[i]='%'+rand()%('z'-'%');           /* create a value between '%' and 'z' */
      }
      created_lock[i++]=' ';

      /* and add a Pk= to look like a nmdc client */
      created_lock[i++]='P';
      created_lock[i++]='k';
      created_lock[i++]='=';
      if((fake_dcpp_version==NULL)||(fake_dcpp_version->len==0))
      {
            for(j=0;j<16;j++)
            {
                  static const char * const valid_char="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?@*+-.,>=&';_]";

                  created_lock[i++]=valid_char[rand()%strlen(valid_char)];
            }
      }
      else
      {
            /* build DC++ like key */
            sprintf(created_lock+i,"DCPLUSPLUS%sABCABC",fake_dcpp_version->str);
            i+=strlen(created_lock+i);
      }

      created_lock[i++]='|';                          /* and the | at the end */
      created_lock[i++]='\0';

      {
            gchar *v;
            int ret;

            v=g_strconcat("$Lock ",created_lock,NULL);

            ret=(send(sck,v,strlen(v),MSG_NOSIGNAL)!=strlen(v));
            g_free(v);
            if(ret)
                  return 1;   /* fails */
      }

      created_lock[ln]='\0';        /* truncate to keep only the useful part */
      return 0;         /* ok */
}

/*************************************/
/* send the length of the given file */
/*************************************************/
/* output: 0=ok, 1=error                         */
/*         if ok, *file_len is the reported size */
/*************************************************/
int send_file_length(int sck,char *filename, unsigned long long *file_len)
{
      GString *out;
      int res;
      struct stat st;

      if(stat(filename,&st))
            return 1;

      (*file_len)=st.st_size;

      out=g_string_new("$FileLength ");
      g_string_sprintfa(out,"%llu|",(unsigned long long)st.st_size);

      res=(send(sck,out->str,out->len,MSG_NOSIGNAL)!=out->len);
      g_string_free(out,TRUE);
      return res;
}


/***********************************/
/* send the data of the given file */
/***********************************/
/* output: 0=ok, 1=error */
/*************************/
int send_file_data(int sck,char *filename, unsigned long long start_pos, unsigned long long file_len,WAIT_ACT *act)
{
      unsigned long long i;
      char buf[8192];               /* must be a multiple of 512 */
      unsigned long long a=file_len-start_pos;
      unsigned long long nb;
      FILE *f;
      int remain;
      char ul_stat[512];
      time_t start_time;
      unsigned long long sent_data=0;
      int ret_code=0;

      start_time=time(NULL);

      f=fopen(filename,"rb");
      if(f==NULL)
            return 1;

      if(fseek(f,start_pos,SEEK_SET))
      {
            ret_code=1;
            goto abrt;
      }

      nb=a/sizeof(buf);
      disp_msg(DEBUG_MSG,"send_file_data","start",NULL);
      for(i=0;i<nb;i++)
      {
            int res;

            res=fread(buf,1,sizeof(buf),f);
            if(res!=sizeof(buf))    /* read error ? */
            {
                  ret_code=1;
                  goto abrt;
            }

            act->last_touch=time(NULL);

            get_ul_slices(bl_semid,sizeof(buf)/512);                                      /* obtain upload authorization */
            res=send(sck,buf,sizeof(buf),MSG_NOSIGNAL /* |MSG_WAITALL */ );

            act->last_touch=time(NULL);
            if(res!=sizeof(buf))
            {
                  ret_code=1;
                  goto abrt;
            }

            sent_data+=res;
            sprintf(ul_stat,"%lu:%llu/%llu/%llu/%llu",act->thread_id,start_pos,file_len,(i+1)*sizeof(buf),a);
            disp_msg(XFER_UL_STAT,NULL,ul_stat,NULL);
      }

      remain=a%sizeof(buf);
      disp_msg(DEBUG_MSG,"send_file_data","partial",NULL);
      if(remain!=0)
      {
            int res;

            res=fread(buf,1,remain,f);
            if(res!=remain)         /* read error ? */
            {
                  ret_code=1;
                  goto abrt;
            }
            
            act->last_touch=time(NULL);

            get_ul_slices(bl_semid,(remain+511)/512);                                     /* obtain upload authorization */
            res=send(sck,buf,remain,MSG_NOSIGNAL /* |MSG_WAITALL */ );

            act->last_touch=time(NULL);
            if(res!=remain)
            {
                  ret_code=1;
                  goto abrt;
            }
            sent_data+=res;
      }
      disp_msg(DEBUG_MSG,"send_file_data","done",NULL);

      abrt:

      if(sent_data!=0)
      {     /* if something was sent, log it */
            FILE *f;
            GString *o;
            G_LOCK_DEFINE_STATIC(done_log);

            o=g_string_new("");
            g_string_sprintf(o,"%s.done",local_dctc_sock_path->str);
            G_LOCK(done_log);

            f=fopen(o->str,"ab");
            if(f!=NULL)
            {
#ifndef __USE_FILE_OFFSET64
                  fprintf(f,"%s|XUL:%llu:%llu:%llu:%llu:%c|%s|%lu\n",
                                          act->remote_nick?act->remote_nick->str:"?Undefined?",
                                          (unsigned long long)time(NULL),(unsigned long long)start_time,start_pos,sent_data,
                                          (ret_code==0)?' ':'E',
                                          filename,file_len);
#else
                  fprintf(f,"%s|XUL:%llu:%llu:%llu:%llu:%c|%s|%llu\n",
                                          act->remote_nick?act->remote_nick->str:"?Undefined?",
                                          (unsigned long long)time(NULL),(unsigned long long)start_time,start_pos,sent_data,
                                          (ret_code==0)?' ':'E',
                                          filename,file_len);
#endif
                  fclose(f);
            }
            G_UNLOCK(done_log);
            g_string_free(o,TRUE);
      }
      fclose(f);
      return ret_code;
}

/************************************/
/* send the data of the given array */
/************************************/
/* output: 0=ok, 1=error */
/*************************/
int send_array_data(int sck,GByteArray *ba,WAIT_ACT *act)
{
      unsigned long int i;
      unsigned long int nb;
      int remain;
      unsigned long cur_pos=0;
      int res;

#define BLOCK_SIZE 8192

      nb=ba->len/BLOCK_SIZE;
      disp_msg(DEBUG_MSG,"send_array_data","start",NULL);
      for(i=0;i<nb;i++)
      {
            act->last_touch=time(NULL);

            get_ul_slices(bl_semid,BLOCK_SIZE/512);                                       /* obtain upload authorization */
            res=send(sck,ba->data+cur_pos,BLOCK_SIZE,MSG_NOSIGNAL /* |MSG_WAITALL */ );

            act->last_touch=time(NULL);
            if(res!=BLOCK_SIZE)
            {
                  abrt:
                  return 1;
            }
            cur_pos+=BLOCK_SIZE;
      }

      remain=ba->len%BLOCK_SIZE;
      disp_msg(DEBUG_MSG,"send_array_data","partial",NULL);
      if(remain!=0)
      {
            act->last_touch=time(NULL);

            get_ul_slices(bl_semid,(remain+511)/512);                                     /* obtain upload authorization */
            res=send(sck,ba->data+cur_pos,remain,MSG_NOSIGNAL /* |MSG_WAITALL */ );

            act->last_touch=time(NULL);
            if(res!=remain)
                  goto abrt;
      }
      disp_msg(DEBUG_MSG,"send_array_data","done",NULL);

      return 0;
}

/* convert path from dos to unix format */
void unconvert_path(char *str)
{
      while(*str)
      {
            if(*str=='\\')
                  *str='/';
            str++;
      }
}

/*******************************************************/
/* process the "$GetListLen " command                  */
/*******************************************************/
/* $GetListLen is always followed by               */
/* a $Send| which is processed by this function    */
/**************************************************************************/
/* this function should return the list of all files shared by the client */
/**************************************************************************/
int com_up_get_list_len_process(const char *cmd,WAIT_ACT *act,int sck,GString *input, char *xtra_param)
{
      GByteArray *cpy_data;
      GString *out;
      int res;
      GString *inp;

      disp_msg(INFO_MSG,"type",cmd,NULL);
      /* get a copy of the compressed shared file list */
      G_LOCK(shared_info);
      if((he3_ls_info==NULL)||(he3_ls_info->len==0))
            cpy_data=NULL;
      else
      {
            cpy_data=g_byte_array_new();
            cpy_data=g_byte_array_append(cpy_data,he3_ls_info->data,he3_ls_info->len);
      }
      G_UNLOCK(shared_info);

      /* it is time to verify if a slot is available */
      LOCK_WRITE(user_info);
      if((dl_on==0)||
            (cpy_data==NULL)        /* nothing to share */
            )
      {
            fail_to_obtain_slot:
            /* no free slot or no download allowed */
            UNLOCK_WRITE(user_info);

            send_dc_line(sck,"$MaxedOut",NULL);

            if(cpy_data!=NULL)
                  g_byte_array_free(cpy_data,TRUE);
            return 1;   /* abort */
      }

      /* lock a slot but only if sharelist download requires a slot */
      if(sharelist_dl_wo_slot==0)
      {
            if(try_to_get_ul_slot(bl_semid)==0)
                  goto fail_to_obtain_slot;
      }
      else
      {
            /* get an upload slot, if one is available, everything works as usual */
            /* else we must have this upload taken into account. If not, at the end */
            /* of the upload, we will free a not allocated upload slot */
            force_get_ul_slot(bl_semid);
      }

      UNLOCK_WRITE(user_info);


      /* send file length */
      if(strcmp(cmd,"$GetListLen"))
            out=g_string_new("$FileLength ");
      else
            out=g_string_new("$ListLen ");
      if(cpy_data==NULL)
            g_string_sprintfa(out,"%lu|",(unsigned long)100000+rand()%500000);
      else
            g_string_sprintfa(out,"%lu|",(unsigned long)cpy_data->len);

      disp_msg(DEBUG_MSG,"reply",out->str,NULL);

      res=send(sck,out->str,out->len,MSG_NOSIGNAL);
      res=(res!=out->len);
      g_string_free(out,TRUE);
      if(res)
      {
            if(cpy_data!=NULL)
                  g_byte_array_free(cpy_data,TRUE);
            free_one_ul_slot(bl_semid);
            return 1;
      }

      /* get command */
      inp=get_a_dc_line(sck);
      if(inp!=NULL)
      {
            disp_msg(INFO_MSG,"strt1",inp->str,NULL);
            if(!strncmp(inp->str,"$Get MyList.DcLst",strlen("$Get MyList.DcLst")))
            {
                  g_string_free(inp,TRUE);
                  out=g_string_new("$FileLength ");
                  if(cpy_data==NULL)
                        g_string_sprintfa(out,"%lu|",(unsigned long)100000+rand()%500000);
                  else
                        g_string_sprintfa(out,"%lu|",(unsigned long)cpy_data->len);
                  res=send(sck,out->str,out->len,MSG_NOSIGNAL);
                  res=(res!=out->len);
                  g_string_free(out,TRUE);
                  if(res)
                  {
                        if(cpy_data!=NULL)
                              g_byte_array_free(cpy_data,TRUE);
                        free_one_ul_slot(bl_semid);
                        return 1;
                  }

                  disp_msg(INFO_MSG,"strt3",NULL);
                  inp=get_a_dc_line(sck);
                  if(inp!=NULL)
                  {
                        disp_msg(INFO_MSG,"strt4",inp->str,NULL);
                        g_string_free(inp,TRUE);
                  }
            }
            else
                  g_string_free(inp,TRUE);
      }

      /* update act info */
      G_LOCK(waiting_action);
      if(act->disp_info==NULL)
            act->disp_info=g_string_new("UL/Shared file list");
      else
            act->disp_info=g_string_assign(act->disp_info,"UL/Shared file list");
      G_UNLOCK(waiting_action);

      disp_msg(XFER_UL_START,"",act->remote_nick->str,"Shared file list",NULL);
      disp_msg(XFER_UL_RUN,"",act->remote_nick->str,"Shared file list",NULL);
      if(send_array_data(sck,cpy_data,act))
      {
            G_LOCK(waiting_action);
            act->disp_info=g_string_assign(act->disp_info,"");
            G_UNLOCK(waiting_action);
            disp_msg(XFER_UL_FATAL,"",act->remote_nick->str,"Shared file list",NULL);

            /* release the slot */
            free_one_ul_slot(bl_semid);

            if(cpy_data!=NULL)
                  g_byte_array_free(cpy_data,TRUE);
            return 1;         /* abort */
      }

      G_LOCK(waiting_action);
      act->disp_info=g_string_assign(act->disp_info,"");
      G_UNLOCK(waiting_action);
      disp_msg(XFER_UL_END,"",act->remote_nick->str,"Shared file list",NULL);

      /* release the slot */
      free_one_ul_slot(bl_semid);

      if(cpy_data!=NULL)
            g_byte_array_free(cpy_data,TRUE);

      /* here is the big change */
      /* before return to the calling function, we wait until something happens on sck */
      /* and we periodically update act->last_touch to avoid connection close */
      {
            time_t end_time;
            fd_set rd;
            int ret;
            struct timeval tv;

#define MAX_WAIT (15*60)            /* wait up to 15 minutes before closing the connection */

            end_time=time(NULL)+MAX_WAIT;

            while(time(NULL)<end_time)
            {
                  FD_ZERO(&rd);
                  FD_SET(sck,&rd);

                  tv.tv_sec=15;
                  tv.tv_usec=0;

                  ret=select(sck+1,&rd,NULL,NULL,&tv);
                  if(ret>0)         /* something to read ? */
                        break;
                  if(ret==-1)
                  {
                        if(errno!=EAGAIN)       /* non recoverable error ? */
                              break;
                  }
            }
      }

      return 0;         /* continue */
}

/*******************************************************/
/* process the "$Get " command                         */
/*******************************************************/
/* $Get file full path$val| is always followed by */
/* a $Send| which is processed by this function    */
/***************************************************/
int com_up_get_process(const char *cmd,WAIT_ACT *act,int sck,GString *input, char *xtra_param)
{
      char *t;
      char *fullpathname;
      long long int val;
      GString *str2;
      unsigned long long file_len;
      int virtual;

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"com_up_get_process","no param",NULL);
            return 1;         /* abort */
      }

      fullpathname=t;
      t=strchr(t,'$');
      if(t==NULL)
      {
            disp_msg(ERR_MSG,"com_up_get_process","invalid param",NULL);
            return 1;         /* abort */
      }

      *t++='\0';
      (strchr(t,'|'))[0]='\0';                  /* never fails because DC line always ends with a | */
      val=strtoll(t,NULL,10)-1;                 /* start position (1=first byte for vb loo^H^H^Husers)*/

      if(!strcmp(fullpathname,"MyList.DcLst"))
      {
            /* this is used by some client versions instead of $GetList */
            /* someone wants our file list */
            return com_up_get_list_len_process(cmd,act,sck,input,NULL);
      }

      disp_msg(DEBUG_MSG,"com_up_get_process","$Get ok",NULL);

      unconvert_path(fullpathname);

      if(!file_in_db(fullpathname,&virtual))          /* is it a shared file ? */
      {
            GString *err_msg;

            err_msg=g_string_new("$Error ");
            g_string_sprintfa(err_msg,"%s no more exists",fullpathname);
            disp_msg(DEBUG_MSG,"com_up_get_process","file not found",err_msg->str,NULL);
            send_dc_line(sck,err_msg->str,NULL);
            g_string_free(err_msg,TRUE);
            return 1;
      }

      /* we always prepend a '/' at the beginning of the fullpathname */
      /* this is done in a very dirty way. Due to the fact that the command is always before the fullname */
      /* and we won't use it anymore, we "accidentally" trash it :) */
      fullpathname--;
      fullpathname[0]='/';

      /* it is time to verify if a slot is available */
      LOCK_WRITE(user_info);
      if((dl_on==0)||(virtual!=0))
      {
            /* no free slot or no download allowed */
            UNLOCK_WRITE(user_info);
      
            send_dc_line(sck,"$MaxedOut",NULL);
            return 1;   /* abort */
      }
      else
      {
            /* lock a slot */
            if(try_to_get_ul_slot(bl_semid)==0)
            {
                  if(!user_has_flag(act->remote_nick->str,"IGNORE_SLOT_LIMIT"))
                  {
                        /* no free slot or no download allowed */
                        UNLOCK_WRITE(user_info);
            
                        send_dc_line(sck,"$MaxedOut",NULL);
                        return 1;   /* abort */
                  }
                  force_get_ul_slot(bl_semid);
            }
      }

      UNLOCK_WRITE(user_info);
#if 0
      send_dc_line(sck,"$Capabilities","SEND_WITH_SIZE",NULL);
#endif

      if(send_file_length(sck,fullpathname,&file_len))
      {
            disp_msg(DEBUG_MSG,"com_up_get_process","fail2",NULL);
            free_one_ul_slot(bl_semid);
            return 1;               /* abort */
      }

      str2=get_a_dc_line(sck);
      if(str2==NULL)
      {
            disp_msg(DEBUG_MSG,"com_up_get_process","fail3",NULL);
            free_one_ul_slot(bl_semid);
            return 1;               /* abort */
      }

      disp_msg(DEBUG_MSG,"com_up_get_process","str2:",str2->str,NULL);
      
      if(!strncmp(str2->str,"$ChgSSP ",strlen("$ChgSSP ")))
      {
            val=strtoll(str2->str+strlen("$ChgSSP "),NULL,10);    /* new start position */
            
            {     /* Send the reply */
                  GString *start_pos_msg;

                  start_pos_msg=g_string_new("$StartPos ");
                  g_string_sprintfa(start_pos_msg,"%lld",val);
                  send_dc_line(sck,start_pos_msg->str,NULL); /* Send the reply */
                  g_string_free(start_pos_msg,TRUE);
            }

            --val; /* new start position minus 1 */          
            
            str2=get_a_dc_line(sck);
            if(str2==NULL)
            {
                  disp_msg(DEBUG_MSG,"com_up_get_process","fail4",NULL);
                  free_one_ul_slot(bl_semid);
                  return 1;               /* abort */
            }
      }

      if(strcmp(str2->str,"$Send|"))
      {
            if(strncmp(str2->str,"$Send ",strlen("$Send ")))
            {
                  g_string_free(str2,TRUE);
                  free_one_ul_slot(bl_semid);
                  return 1;         /* abort */
            }

            /* we have received a $Send with a size after */
            {
                  unsigned long long wanted_size;

                  wanted_size=strtoull(str2->str+strlen("$Send "),NULL,10);

                  file_len=val+wanted_size;           /* adjust "Real" file len to limit the number of bytes sent by send_file_data */
                  /* NOTE: if original file_len is smaller than the one computed here, the transfer will end */
                  /*       on error when send_file_data reaches the end of the file so there is no problem */
            }
      }
      
      g_string_free(str2,TRUE);

      /* update act info */
      G_LOCK(waiting_action);
      if(act->disp_info==NULL)
            act->disp_info=g_string_new("UL/");
      else
            act->disp_info=g_string_assign(act->disp_info,"UL/");
      g_string_sprintfa(act->disp_info,"%s|",fullpathname);
      G_UNLOCK(waiting_action);

      disp_msg(XFER_UL_START,"",act->remote_nick->str,fullpathname,NULL);
      {
            char tmp[510];
            sprintf(tmp,"%lu",(unsigned long)(act->thread_id));
            disp_msg(XFER_UL_RUN,NULL,tmp,act->remote_nick->str,act->disp_info->str,NULL);
      }
      
      if(send_file_data(sck,fullpathname,val,file_len,act))
      {
            G_LOCK(waiting_action);
            act->disp_info=g_string_assign(act->disp_info,"");
            G_UNLOCK(waiting_action);
            disp_msg(XFER_UL_FATAL,"",act->remote_nick->str,fullpathname,NULL);

            /* release the slot */
            free_one_ul_slot(bl_semid);
            return 1;         /* abort */
      }

      G_LOCK(waiting_action);
      act->disp_info=g_string_assign(act->disp_info,"");
      G_UNLOCK(waiting_action);
      disp_msg(XFER_UL_END,"",act->remote_nick->str,fullpathname,NULL);

      /* release the slot */
      free_one_ul_slot(bl_semid);
      return 0;         /* continue */
}

/*********************************************************************/
/* process the "$GetFileLength " command (it is an extended command) */
/*********************************************************************/
/* $GetFileLength file full path| */
/**********************************/
int com_up_getfilelength_process(const char *cmd,WAIT_ACT *act,int sck,GString *input, char *xtra_param)
{
      char *t;
      char *fullpathname;
      unsigned long long file_len;
      int virtual;

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"com_up_get_process","no param",NULL);
            return 1;         /* abort */
      }

      fullpathname=t;

      (strchr(t,'|'))[0]='\0';                  /* never fails because DC line always ends with a | */

      disp_msg(DEBUG_MSG,"com_up_getfilelength_process","$Get ok",NULL);

      unconvert_path(fullpathname);

      if(!file_in_db(fullpathname,&virtual))          /* is it a shared file ? */
      {
            GString *err_msg;

            err_msg=g_string_new("$Error ");
            g_string_sprintfa(err_msg,"%s no more exists",fullpathname);
            disp_msg(DEBUG_MSG,"com_up_getfilelength_process","file not found",err_msg->str,NULL);
            send_dc_line(sck,err_msg->str,NULL);
            g_string_free(err_msg,TRUE);
            return 1;
      }

      /* we always prepend a '/' at the beginning of the fullpathname */
      /* this is done in a very dirty way. Due to the fact that the command is always before the fullname */
      /* and we won't use it anymore, we "accidentally" trash it :) */
      fullpathname--;
      fullpathname[0]='/';

      if(send_file_length(sck,fullpathname,&file_len))
      {
            disp_msg(DEBUG_MSG,"com_up_getfilelength_process","fail2",NULL);
            return 1;               /* abort */
      }

      return 0;         /* continue */
}

/**************************************************/
/* process the $Capabilities from the xfer socket */
/**************************************************/
int xfer_capabilities_process(const char *cmd,WAIT_ACT *act,int sck,GString *input, char *xtra_param)
{
      char *t;
      gchar **fields;
      int i;

      /* create empty array for capabilities */
      if(act->cap_ptr!=NULL)
            g_ptr_array_free(act->cap_ptr,TRUE);
      act->cap_ptr=g_ptr_array_new();

      if(act->cap_str!=NULL)
            g_string_chunk_free(act->cap_str);
      act->cap_str=g_string_chunk_new(32);

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      fields=g_strsplit(t,"$",0);
      i=0;
      while(fields[i]!=NULL)
      {
            t=strchr(fields[i],'|');
            if(t!=NULL)
                  *t='\0';
            if(strlen(fields[i])>1)
            {
                  g_ptr_array_add(act->cap_ptr,g_string_chunk_insert(act->cap_str,fields[i]));
            }
            i++;
      }

      g_strfreev(fields);
      return 0;   /* ok */
}


/**************************************************************************/
/* when we enter in "Upload" direction, this function process all uploads */
/**************************************************************************/
void manage_com_upload(WAIT_ACT *act)
{
      static CMD_REPLY_ACT com_upload_cmd[]=    {
                                                                  {"$Get "           , sizeof("$Get ")-1           ,com_up_get_process          ,(char*)NULL},
                                                                  {"$Capabilities "  , sizeof("$Capabilities ")-1  ,xfer_capabilities_process   ,(char*)NULL},
                                                                  {"$GetListLen"     , sizeof("$GetListLen")-1     ,com_up_get_list_len_process ,(char*)NULL},
                                                                  {"$GetFileLength " , sizeof("$GetFileLength ")-1 ,com_up_getfilelength_process,(char*)NULL},
                                                                  {NULL,0,NULL},
                                                            };
      GString *input;
      int i;
      int res;


      while(1)
      {
            input=get_a_dc_line(act->sock_fd);
            if(input==NULL)
                  return;

            disp_msg(DEBUG_MSG,"manage_com_upload",input->str,NULL);

            i=0;
            while(com_upload_cmd[i].cmd!=NULL)
            {
                  if(!strncmp(input->str,com_upload_cmd[i].cmd,com_upload_cmd[i].cmd_len))
                  {
                        res=com_upload_cmd[i].fnc(com_upload_cmd[i].cmd, act, act->sock_fd,input, com_upload_cmd[i].xtra_param);
                        if(res==1)
                        {
                              g_string_free(input,TRUE);
                              return;
                        }
                  }
                  i++;
            }
            g_string_free(input,TRUE);
      }
}

#define min(a,b)        (((a)<(b))?(a):(b))

/***************************************************************/
/* take a "$FileLength xxx" string and return the value of xxx */
/***************************************************************/
/* output: 0=ok (*file_size=xxx), 1=error */
/******************************************/
int extract_filelength(char *str, unsigned long *file_size)
{
      if(strncmp(str,"$FileLength ",sizeof("$FileLength ")-1))
            return 1;

      str+=sizeof("$FileLength ")-1;
      SKIP_SPACE(str)

      if(*str=='\0')
            return 1;

      *file_size=strtoul(str,NULL,10);

      return 0;
}

/************************************/
/* process the given WAITING_REVCON */
/*************************************************************/
/* 0=operation done, 1=operation killed or error encountered */
/*************************************************************/
int start_a_xdownload(WAIT_ACT *act, WAIT_REVCON *act_to_do)
{
      char **fields;
      unsigned int gdl_id;
      GString *get_str;
      GString *lfile;
      GString *input=NULL;
      unsigned long remote_file_size;
      unsigned long start_position;
      FILE *local_file=NULL;
      int no_link=1;
      int have_worked=0;

      disp_msg(DEBUG_MSG,"start_a_xdownload",act_to_do->action_to_do->str,NULL);
      G_LOCK(waiting_action);
      act->run_task=act_to_do;                        /* associate a task with this thread */
      G_UNLOCK(waiting_action);

      /* expand and decode argument */
      /* there is always 3 fields, never less, never more */
#ifndef WITH_GLIB2
   fields=g_strsplit(act_to_do->action_to_do->str,"|",3);     /* GLIB2 fixed */
#else
   fields=g_strsplit(act_to_do->action_to_do->str,"|",3+1);   /* GLIB2 fix */ 
#endif

      gdl_id=strtoul(fields[1],NULL,10);
      /* fields[2] is the remote nick */

      if(do_gdl_start(gdl_id, fields[2], act->thread_id, &get_str,&lfile,&start_position)==0)
      {
            int is_fatal=0;
            unsigned long total_pos=0;

            /* we have a range */
            send_dc_line(act->sock_fd,get_str->str,NULL);
            if(get_str!=NULL)
                  g_string_free(get_str,TRUE);

            input=get_a_dc_line(act->sock_fd);                    /* after the $Get, we should receive a $FileLength line */
            if( (input==NULL) ||
                   (!strcmp(input->str,"$MaxedOut|"))
              )
            {
                  end_on_error:

                  if(local_file!=NULL)
                  {
                        fclose(local_file);
                        local_file=NULL;
                  }

                  do_gdl_fail(gdl_id,fields[2],lfile->str,is_fatal);
                  if(lfile!=NULL)
                        g_string_free(lfile,TRUE);
                  goto leave;
            }

            if(!strncmp(input->str,"$Error ",7))
            {
                  /* on $Error, no retry is performed */
                  is_fatal=1;
                  goto end_on_error;
            }

            if(extract_filelength(input->str,&remote_file_size))
                  goto end_on_error;

            local_file=fopen(lfile->str,"wb");
            if(local_file==NULL)
                  goto end_on_error;

            /* now, we want the file */
            send_dc_line(act->sock_fd,"$Send",NULL);

            /* update act info */
            G_LOCK(waiting_action);
            if(act->disp_info==NULL)
                  act->disp_info=g_string_new(act_to_do->action_to_do->str);
            else
                  act->disp_info=g_string_assign(act->disp_info,act_to_do->action_to_do->str);
            G_UNLOCK(waiting_action);

            /* the following code copies data from the remote file descriptor into the local file descriptor */
            for(;;)
            {
                  char buf[8192];
                  int ret;
                  unsigned int amount;

                  amount=gdl_get_amount(gdl_id,act->thread_id,total_pos);           /* get the size we must download, at most 8KB */
                                                                                                                                          /* at the same time, update the current position */
                  if(amount==0)
                        break;

                  get_dl_slices(bl_semid,(amount+1023)/1024);

                  act->last_touch=time(NULL);
                  ret=recv(act->sock_fd,buf,amount,MSG_WAITALL|MSG_NOSIGNAL);
                  if((ret==-1)||(ret==0))
                  {     /* error or nothing received */
                        goto end_on_error;
                  }
                  act->last_touch=time(NULL);
                  if(fwrite(buf,1,ret,local_file)!=ret)
                  {
                        disp_msg(ERR_MSG,"xcopie_fd_to_file","disk full",NULL);
                        goto end_on_error;
                  }
                  total_pos+=ret;
            }

            fclose(local_file);           /* close the file before the call to success */
            local_file=NULL;

            if(lfile!=NULL)
            {
                  g_string_free(lfile,TRUE);
                  lfile=NULL;
            }

            do_gdl_success(gdl_id, act->thread_id,1);       /* currently, it is not yet possible to link multiple downloads on the same link */

            /* it is possible to link task in 1 case. If our download end position is the remote file size */
            /* because in this case, the remote client expects a client. In all other cases, it still sends data */
            if((start_position+total_pos)==remote_file_size)
                  no_link=0;

            have_worked=1;
      }
      leave:

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

      if(have_worked)
      {
            gdl_wake_up_sources(fields[2],0);
      }

      g_strfreev(fields);
      G_LOCK(waiting_action);
      if(act->disp_info)
      {
            g_string_free(act->disp_info,TRUE);
            act->disp_info=NULL;
      }
      act->run_task=NULL;                                   /* dissociate the task from this thread */
      G_UNLOCK(waiting_action);
      return no_link;         /* never link commands (except in few cases) */
}

/**************************************************************************************/
/* read amount bytes from remote file descriptor and write them into given byte array */
/**************************************************************************************/
/* output: 2=network error, 0=ok                                    */
/*         in all cases, (*ba) will be a newly allocated GByteArray */
/********************************************************************/
/* warning: don't use read to get data from network, prefer recv */
/*****************************************************************/
int copie_fd_to_bytearray(int remote, GByteArray **ba, unsigned long amount,WAIT_ACT *act)
{
      int pos=0;
      int ret;
      unsigned long nb;

      (*ba)=g_byte_array_new();
      (*ba)=g_byte_array_set_size((*ba),amount);

      while(amount!=0)
      {
            nb=min(amount,8192);          /* never read more than 8KB at the same time. */

            /* touch the action slot to avoid timeout */
            act->last_touch=time(NULL);
#if 0
            ret=recv(remote,(*ba)->data+pos,nb,MSG_WAITALL|MSG_NOSIGNAL);
#else
            ret=recv(remote,(*ba)->data+pos,nb,MSG_NOSIGNAL);
            printf("%d (nb:%lu, amount: %lu)\n",ret,nb,amount);
#endif
            
            if((ret==-1)||(ret==0))
            {     /* error or nothing received */
                  if(ret==-1)
                        perror("copie_fd_to_bytearray 1");
                  disp_msg(ERR_MSG,"copie_fd_to_bytearray","connection closed (1)",NULL);
                  return 2;
            }
            act->last_touch=time(NULL);

            pos+=ret;
            amount-=ret;
      }
      
      return 0;
}

/***********************************************/
/* convert \r\n into \n in the given string    */
/* In fact, we just remove all \r we encounter */
/***********************************************/
GString *my_g_string_dos2unix(GString *str)
{
      if(str!=NULL)
      {
            int i=0;

            while(i<str->len)
            {
                  if(str->str[i]=='\r')
                  {
                        str=g_string_erase(str,i,1);
                  }
                  else
                        i++;
            }
      }
      return str;
}

/****************************************************************************************************/
/* take the file list (in content), save it in the cache and send the "cache" message to the client */
/****************************************************************************************************/
void generate_shared_file_list(WAIT_REVCON *act_to_do, GString *content)
{
      GString *final_ls_content;
      GString *dir_part;
      char *ptr;
      char *nxt_ptr;
      int lvl;
      int dir_part_lvl=0;
      unsigned long long ttl_size=0;
      char *u;

      content=my_g_string_dos2unix(content);

      final_ls_content=g_string_new("");
      dir_part=g_string_new("");
      
      ptr=strtok_r(content->str,"\n",&nxt_ptr);
      while(ptr!=NULL)
      {
            /* for each entry, the number of \t at the beginning of the line */
            /* is the directory level (==the number of \ in dir_part) */
            lvl=0;
            while((*ptr)&&((*ptr)=='\t'))
            {
                  ptr++;
                  lvl++;
            }

            /* adjust dir_part to the given directory level */
            if(dir_part_lvl>lvl)
            {
                  if(lvl==0)
                  {     /* go back to the root */
                        dir_part=g_string_assign(dir_part,"");
                        dir_part_lvl=0;
                  }
                  else
                  {
                        int p;
                        char *last_level=NULL;

                        p=1;
                        last_level=strchr(dir_part->str,'\\');

                        while((p!=lvl)&&(last_level!=NULL))
                        {
                              last_level=strchr(last_level+1,'\\');
                              p++;
                        }

                        if(last_level!=NULL)
                        {
                              /* cut the string just after the wanted \\ */
                              dir_part=g_string_truncate(dir_part,(last_level+1)-(dir_part->str));
                              dir_part_lvl=lvl;
                        }
                  }
            }
            

            if((u=strchr(ptr,'|'))!=NULL)
            {
                  /* it is a file entry */
                  g_string_sprintfa(final_ls_content,"%s%s\n",dir_part->str,ptr);
                  ttl_size+=strtoul(u+1,NULL,10);
            }
            else
            {
                  /* it is a directory entry */
                  g_string_sprintfa(dir_part,"%s\\",ptr);
                  dir_part_lvl++;
            }
            ptr=strtok_r(NULL,"\n",&nxt_ptr);
      }

      if(save_ls_file(act_to_do->remote_nick->str,final_ls_content,ttl_size))
            disp_msg(ERR_MSG,"","Fail to save /LS result for ","|s",act_to_do->remote_nick->str,NULL);
      else
            disp_msg(LS_CACHE_MSG,"",act_to_do->remote_nick->str,NULL);

      g_string_free(final_ls_content,TRUE);

      g_string_free(dir_part,TRUE);
}

/************************************/
/* process the given WAITING_REVCON */
/*************************************************************/
/* 0=operation done, 1=operation killed or error encountered */
/*************************************************************/
int start_a_ls(WAIT_ACT *act, WAIT_REVCON *act_to_do)
{
      GString *input=NULL;
      GString *content=NULL;
      unsigned long file_size;
      int ret;
      int on_retryable_error=0;
      GByteArray *he3_data=NULL;
      int ret_value=1;

      disp_msg(DEBUG_MSG,"start_a_ls",act_to_do->action_to_do->str,NULL);
      G_LOCK(waiting_action);
      act->run_task=act_to_do;                        /* associate a task with this thread */
#if 0
      act->disp_info=NULL;                                  /* no more necessary when a multi-download occurs */
#endif
      G_UNLOCK(waiting_action);

      send_dc_line(act->sock_fd,"$Get","MyList.DcLst$1",NULL);

      disp_msg(XFER_LS_START,"",act_to_do->remote_nick->str,NULL);
      input=get_a_dc_line(act->sock_fd);                    /* after the $Get, we should receive a $FileLength line */
      if(input==NULL)
      {
            on_retryable_error=1;         /* retry the command */
            leave_with_msg:
            disp_msg(XFER_LS_FATAL,"",act_to_do->remote_nick->str,NULL);
            goto leave;
      }

      disp_msg(DEBUG_MSG,"",input->str,NULL);

      if(!strcmp(input->str,"$MaxedOut|"))
      {
            on_retryable_error=1;         /* retry the command */
            disp_msg(XFER_LS_FATAL,"",act_to_do->remote_nick->str,"No more slot",NULL);
            goto leave;
      }

      if(extract_filelength(input->str,&file_size))
      {
            on_retryable_error=1;         /* retry the command */
            goto leave_with_msg;
      }

      /* now, we want the file */
      send_dc_line(act->sock_fd,"$Send",NULL);

      /* update act info */
      G_LOCK(waiting_action);
      if(act->disp_info==NULL)
            act->disp_info=g_string_new("LS/");
      else
            act->disp_info=g_string_assign(act->disp_info,"LS/");
      G_UNLOCK(waiting_action);
      
      disp_msg(XFER_LS_RUN,"",act_to_do->remote_nick->str,NULL);
      ret=copie_fd_to_bytearray(act->sock_fd,&he3_data,file_size,act);
      if(ret)
      {
            on_retryable_error=1;         /* retry the command */
            G_LOCK(waiting_action);
            act->disp_info=g_string_assign(act->disp_info,"");
            G_UNLOCK(waiting_action);
            goto leave_with_msg;
      }
      
      G_LOCK(waiting_action);
      act->disp_info=g_string_assign(act->disp_info,"");
      G_UNLOCK(waiting_action);

      disp_msg(XFER_LS_END,"",act_to_do->remote_nick->str,NULL);

      content=decode_he3_data(he3_data);
      if(content!=NULL)
      {
            generate_shared_file_list(act_to_do,content);
      }
      else
      {
            disp_msg(ERR_MSG,"","Invalid shared file list.",NULL);
      }
      ret_value=0;            /* ok */
      leave:

      if(act->running!=3)           /* task not killed */
      {
            if(on_retryable_error)
            {     /* if the error can be recovered, we will try to restart the command a bit later */
                  GString *sim;
      
                  sim=g_string_new("");
      
                  g_string_sprintf(sim,"/LS %s", act_to_do->remote_nick->str);
      
                  /* wait 30 seconds before retrying */
                  if(add_gts_entry(act_to_do->remote_nick->str,sim->str,30))
                        add_new_sim_input(30,sim->str);           /* fail to queue in the GTS, use sim_input instead */
      
                  g_string_free(sim,TRUE);
            }
      }

      if(input)
            g_string_free(input,TRUE);
      if(he3_data)
            g_byte_array_free(he3_data,TRUE);
      if(content)
            g_string_free(content,TRUE);
      
      G_LOCK(waiting_action);
      if(act->disp_info)
      {
            g_string_free(act->disp_info,TRUE);
            act->disp_info=NULL;
      }
      act->run_task=NULL;                                   /* dissociate the task from this thread */
      G_UNLOCK(waiting_action);

      return ret_value;
}

/***********************/
/* start a queued task */
/******************************/
/* output: 0=task done        */
/*       !=0=unable to comply */
/******************************/
int run_action_with_remote_side(WAIT_ACT *act,WAIT_REVCON *act_to_do)
{
      char *t;

      t=act_to_do->action_to_do->str;           /* pointer on the action to perform */

      if(!strncmp(t,"DL/",3))
#if 1
      {
            disp_msg(ERR_MSG,"start_a_download","this function is deprecated",NULL);
            return 1;
      }
#else
            return start_a_download(act,act_to_do);
#endif
      else if(!strncmp(t,"LS/",3))
            return start_a_ls(act,act_to_do);
      else if(!strncmp(t,"XDL|",4))
            return start_a_xdownload(act,act_to_do);
      return 1;
}

/***************************************************/
/* send this client capabilities to the remote one */
/***************************************************/
void send_client_capabilities(WAIT_ACT *act)
{
      GString *str;

#if 0
      str=g_string_new("SEND_WITH_SIZE");
#else
      str=g_string_new("GetFileLength$ChgSSP");
#endif

      send_dc_line(act->sock_fd,"$Capabilities",str->str,NULL);
      g_string_free(str,TRUE);
}

/**************************************************************/
/* copy the given wait_recon into GTS (or sim_input on error) */
/**************************************************************/
void requeue_act_to_do(WAIT_REVCON *act_to_do)
{
      if(act_to_do!=NULL)
      {     
#if 0
            /* if the error can be recovered, we will try to restart the command a bit later */
            if(!strncmp(act_to_do->action_to_do->str,"DL/",3))
            {
                  GString *sim, *local, *org_remote;
                  char *t;
                  char sep;

                  sep=act_to_do->action_to_do->str[sizeof("DL/")-1];    /* get the separator */

                  local=g_string_new(act_to_do->action_to_do->str + sizeof("DL/") -1 + 1 /* ignore the separator */ );        
                  t=strrchr(local->str,sep);                /* can't fail */
                  local=g_string_truncate(local,t-local->str);          /* truncate the size of the string in local */
      
                  t=strrchr(local->str,sep);                /* can't fail */
                  org_remote=g_string_new(t+1);
                  local=g_string_truncate(local,t-local->str);          /* truncate the remote path of the string in local */
      
                  sim=g_string_new("");
      
                  /* we use | as separator because it cannot appear anywhere */
                  g_string_sprintf(sim,"/DL |%s|%s|%s|", act_to_do->remote_nick->str, local->str, org_remote->str);

                  /* @@@ add the xtra information at the end of the /DL */
                  {
                        UCNX tp;
                        GString *xt;

                        xt=get_xtra_information_cnx(act_to_do->remote_nick->str,&tp);
                        if((tp==ACTIVE)&&(xt!=NULL)&&(xt->len!=0))
                        {
                              g_string_sprintfa(sim,"|%s",xt->str);
                        }
                        if(xt!=NULL)
                              g_string_free(xt,TRUE);
                  }

                  /* wait 30 seconds before retrying */
                  if(add_gts_entry(act_to_do->remote_nick->str,sim->str,30))
                        add_new_sim_input(30,sim->str);           /* fail to queue in the GTS, use sim_input instead */
      
                  g_string_free(sim,TRUE);
                  g_string_free(local,TRUE);
                  g_string_free(org_remote,TRUE);
            }
            else 
#endif
            if(!strncmp(act_to_do->action_to_do->str,"LS/",3))
            {
                  GString *sim;
                  sim=g_string_new("");

                  g_string_sprintf(sim,"/LS %s", act_to_do->remote_nick->str);

                  /* wait 30 seconds before retrying */
                  if(add_gts_entry(act_to_do->remote_nick->str,sim->str,30))
                        add_new_sim_input(30,sim->str);           /* fail to queue in the GTS, use sim_input instead */

                  g_string_free(sim,TRUE);
            }
            else if(!strncmp(act_to_do->action_to_do->str,"XDL",3))
            {
                  gchar **fields;
                  disp_msg(DEBUG_MSG,"requeue_act_to_do","XDL requeue",NULL);
                  
                  fields=g_strsplit(act_to_do->action_to_do->str,"|",0);
                  if((fields[0]!=NULL)&&(fields[1]!=NULL)&&(fields[2]!=NULL))
                  {
#if 0
                        /* fail a GDL entry for the given nickname (no filename provided) */
                        do_gdl_fail(strtoul(fields[1],NULL,10),fields[2],NULL,0);
#else
                        do_gdl_abort(strtoul(fields[1],NULL,10),fields[2]);
#endif
                  }
                  g_strfreev(fields);
            }
      }
}


/******************************/
/* thread managing connect_me */
/******************************/
void *connect_me_thread(WAIT_ACT *act)
{
      GString *input=NULL;
      char c_lock[256];
      int i;
      WAIT_REVCON *act_to_do=NULL;

      disp_msg(DEBUG_MSG,"connect_me_thread","started","|lu",act,NULL);
      
      while(act->thread_id==0)
            usleep(1000);                       /* wait until the main process has set the thread_id in act */

      disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"continued",NULL);
      act->running=1;
      act->last_touch=time(NULL);

      /* add act to the waiting task array */
      G_LOCK(waiting_action);
      g_ptr_array_add(waiting_action,act);
      G_UNLOCK(waiting_action);
      disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"act added",NULL);

      /* send our name */
      if(send_nick(act->sock_fd))
      {
            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"fail to send nick",NULL);
            goto thread_over;
      }

      disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"nick sent",NULL);

      if(send_a_lock(act->sock_fd,c_lock))
      {     /* send a lock to the remote side */
            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"fail to send lock",NULL);
            goto thread_over;
      }

      disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"lock sent",NULL);

      input=get_a_dc_line(act->sock_fd);                    /* get the "$MyNick line */
      if(input==NULL)
      {
            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"no incoming data",NULL);
            goto thread_over;
      }
      disp_msg(DEBUG_MSG,"connect_me_thread",input->str,NULL);

      /* we should have received "$MyNick xxxx|" */
      if(strncmp("$MyNick ",input->str,8))
      {
            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"$Nick not found",NULL);
            if(input!=NULL)
            {
                  g_string_free(input,TRUE);
                  input=NULL;
            }
            goto thread_over;
      }

      /* My nick is here */
      input=g_string_erase(input,0,8);          /* remove the "$MyNick " at the beginning */
      input=g_string_truncate(input,input->len-1);          /* remove the trailing | */

      if(user_has_flag(input->str,"IGNORE_XFER"))
      {
            g_string_free(input,TRUE);
            input=NULL;
            goto thread_over;
      }

      G_LOCK(waiting_action);
      act->remote_nick=input;                                     /* set the remote nick */
      G_UNLOCK(waiting_action);

      if(act->cnx_type==CNX_ON_CONNECT)
      {     /* remote user actif */
            set_xtra_information_cnx(act->remote_nick->str,ACTIVE,act->remote_addr->str);

            add_uaddr_entry(act->remote_nick->str,act->remote_addr->str);
      }

      input=get_a_dc_line(act->sock_fd);        /* get the $Lock line */
      if(input==NULL)
      {
            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"$Lock not found",NULL);
            goto thread_over;
      }
      disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,input->str,NULL);

      /* ========================================================================================================================== */
      /* lot of changes in the version 0.64 here */
      {
            unsigned int my_level=rand()%65000+535;
            unsigned int remote_level;
            MY_DIR remote_dir, our_dir;

            {
                  GString *tmp;

                  act_to_do=get_action_to_do(act->remote_nick);

                  if(act_to_do==NULL)
                  {
                        /* nothing to do, send a $Direction Upload */
                        our_dir=MY_DIR_UPLOAD;
                        tmp=g_string_new("$Direction Upload ");
                  }
                  else
                  {
                        /* sth to do, send a $Direction Download */
                        our_dir=MY_DIR_DOWNLOAD;
                        tmp=g_string_new("$Direction Download ");
                  }

                  g_string_sprintfa(tmp,"%u|",my_level);
#if 0
                  write(act->sock_fd,tmp->str,tmp->len);                /* @@@ replaced by send */
#else
                  send(act->sock_fd,tmp->str,tmp->len,MSG_NOSIGNAL);
#endif
                  g_string_free(tmp,TRUE);                        /* tmp is no more used */
            }

            /* retrieve remote side $Direction */
            {
                  GString *input2=NULL;

                  if((input2=get_a_dc_line(act->sock_fd))==NULL)
                  {
                        if(act_to_do!=NULL)
                        {
                              prepend_action_to_do(act_to_do);                /* reput the action at the beginning of the waiting action */
                              act_to_do=NULL;         /* reset this field else requeue will also put it inside queue and worse, */
                                                                        /* the prepended and the append action to do are pointers on the same structure */
                                                                        /* when one is freed, the second will probably crash the client */
                        }
                        disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"no Direction",NULL);
                        goto requeue_and_thread_over;
                  }
      
                  remote_dir=decode_direction(input2,&remote_level);
                  if(remote_dir==MY_DIR_ERROR)
                  {
                        disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"no valid Direction",input2->str,NULL);
                        g_string_free(input2,TRUE);
                        goto requeue_and_thread_over;
                  }
                  g_string_free(input2,TRUE);
            }

            /* we have 4 cases (my:remote):   (DIR_UPLOAD:DIR_UPLOAD) (DIR_UPLOAD:DIR_DOWNLOAD) (DIR_DOWNLOAD:DIR_UPLOAD) (DIR_DOWNLOAD:DIR_DOWNLOAD) */
            if((our_dir==MY_DIR_UPLOAD)&&(remote_dir==MY_DIR_UPLOAD))
            {
                  /* nobody wants to download, ends the connection */
                  disp_msg(INFO_MSG,"connect_me_thread","Null download connection",act->remote_nick->str,NULL);

                  /* if I am MY_DIR_UPLOAD, act_to_do==NULL */
                  g_string_free(input,TRUE);
                  input=NULL;
                  goto thread_over;
            }

            if((our_dir==MY_DIR_DOWNLOAD)&&(remote_dir==MY_DIR_DOWNLOAD))
            {
                  /* both wants to download, the one with the higher level wins */
                  disp_msg(INFO_MSG,"connect_me_thread","twin download connection","|lu",(unsigned long)my_level,"|lu",(unsigned long)remote_level,NULL);

                  if(remote_level>my_level)
                  {
                        /* the remote side wins, I switch to upload mode */
                        if(act_to_do!=NULL)
                        {
                              prepend_action_to_do(act_to_do);                /* reput the action at the beginning of the waiting action */
                              act_to_do=NULL;
                        }
                        our_dir=MY_DIR_UPLOAD;
                        disp_msg(INFO_MSG,"connect_me_thread","twin download connection","low priority:switching to upload",NULL);
                  }
            }

            if(our_dir==MY_DIR_DOWNLOAD) {
                  set_tos_sock(act->sock_fd, dl_tos);
            } else {
                  set_tos_sock(act->sock_fd, ul_tos);
            }
            send_client_capabilities(act);

            /* now, reply to the first $Lock. It seems strange but we must do this in this order or it doesn't work */
            if((i=do_unlock_access(act->sock_fd,input))!=0)
            {
                  disp_msg(ERR_MSG,"sub_lock_process","|lu",(unsigned long)act->thread_id,"unable to unlock","|d",(int)i,"nick","|s",act->remote_nick->str,NULL);
                  goto requeue_and_thread_over;
            }

            /* now, we must process incoming string until having the $Key */
            do
            {
                  /* free the currently allocated string, for the first loop, we free the $Lock */
                  g_string_free(input,TRUE);
                  
                  if((input=get_a_dc_line(act->sock_fd))==NULL)
                  {
                        disp_msg(DEBUG_MSG,"connect_me_thread","no input",NULL);
                        requeue_and_thread_over:

                        if(input!=NULL)
                              g_string_free(input,TRUE);          /* no more useful. All goto still has an allocated input */

                        if((our_dir==MY_DIR_DOWNLOAD)&&(act_to_do!=NULL))
                              requeue_act_to_do(act_to_do);
                        goto thread_over;
                  }

                  /* decode optionnal keywords */
                  if(!strncmp(input->str,"$Capabilities ",strlen("$Capabilities ")))
                  {
                        printf("perform capabilities\n");
                        xfer_capabilities_process("$Capabilities ",act,act->sock_fd,input,NULL);
                  }
                  else if(strncmp(input->str,"$Key ",5))
                  {
                        printf("Unknown keyword: %s\n",input->str);
                  }

            }while(strncmp(input->str,"$Key ",5));

            /* we have received the $Key string. It is the end of the handshake */

            disp_msg(DEBUG_MSG,"connect_me_thread",input->str,NULL);

            /* allow lazy key check if flag allows it and if it is a download only */
            if(verify_key(c_lock,input,  (((our_dir==MY_DIR_DOWNLOAD) && (with_lazy_key_check))?1:0) ))
            {
                  disp_msg(ERR_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"Key invalid","nick","|s",act->remote_nick->str,NULL);
                  goto requeue_and_thread_over;
            }
            g_string_free(input,TRUE);                      /* no more useful */
            input=NULL;

            disp_msg(DEBUG_MSG,"connect_me_thread","|lu",(unsigned long)act->thread_id,"$Key ok, DC connected",NULL);

            /* ok, now, we are connected, we can do what we want */
            if(our_dir==MY_DIR_UPLOAD)
            {     /* it is an upload, the remote side drives the connection */
                  disp_msg(DEBUG_MSG,"connect_me_thread","beginning upload",NULL);
                  manage_com_upload(act);
            }
            else
            {     /* it is a download, what to do ? */
                  disp_msg(DEBUG_MSG,"connect_me_thread","beginning download",NULL);
                  while(act_to_do!=NULL)
                  {
                        int cmd_result;

                        cmd_result=run_action_with_remote_side(act,act_to_do);
                        free_action_to_do(act_to_do,0);
                        if(cmd_result==0)
                        {
                              if(act->cnx_type==CNX_ON_CONNECT)
                              {     /* remote user actif, touch uaddr entry to avoid expiration of usable address */
                                    set_xtra_information_cnx(act->remote_nick->str,ACTIVE,act->remote_addr->str);
                        
                                    add_uaddr_entry(act->remote_nick->str,act->remote_addr->str);
                              }

                              /* the previous command exits successfully, we can link it with another one */
                              act_to_do=get_download_action_to_do(act->remote_nick);
                        }
                        else
                              act_to_do=NULL;
                  }
            }
      }

      /* ========================================================================================================================== */
      /* thread is over */
      thread_over:
      G_LOCK(waiting_action);
      g_ptr_array_remove_fast(waiting_action,act);
      G_UNLOCK(waiting_action);
      free_wait_act(act);

      disp_msg(REFRESH_MSG,NULL,NULL);
      pthread_exit(NULL);
}


/*************************************************************************************/
/* when a remote host ask this client to open a connection to himself, this function */
/* opens the connection and create a thread to manage it.                            */
/*************************************************************************************/
void *do_connect_to_me(GString *host_port)
{
      int sock_fd;
      WAIT_ACT *nw_act;
      pthread_attr_t thread_attr;

      char *hst;
      char *p;
      unsigned short port;

      disp_msg(DEBUG_MSG,"do_connect_to_me","host",host_port->str,NULL);

      hst=strdup(host_port->str);
      p=strchr(hst,':');                              /* can never fail */
      port=strtoul(p+1,NULL,10);          
      *p='\0';

      disp_msg(DEBUG_MSG,"do_connect_to_me","host",hst,"port",p+1,NULL);
      
      sock_fd=create_and_open_sock_on(hst,port,0);
      if(sock_fd<0)
      {
            delete_uaddr_entry_by_name(hst);                /* fail to open remote address, invalid the uaddr entry to avoid infinite tries */
                                                                                          /* with an invalid address */
            disp_msg(DEBUG_MSG,"do_connect_to_me","unable to contact",hst,NULL);
            free(hst);
            return NULL;
      }
      free(hst);

      /* ok, remote host connect to the newly created socket */
      nw_act=malloc(sizeof(WAIT_ACT));
      if(nw_act==NULL)
      {
            disp_msg(ERR_MSG,"do_connect_to_me","malloc failed",NULL);
            shutdown(sock_fd,2);
            close(sock_fd);
            return NULL;
      }

      disp_msg(DEBUG_MSG,"do_connect_to_me","creating thread info","|lu",(unsigned long)nw_act,NULL);
      nw_act->running=3;                  /* thread init */
      nw_act->cnx_type=CNX_ON_CONNECT;
      nw_act->last_touch=time(NULL);
      nw_act->thread_id=0;
      nw_act->sock_fd=sock_fd;
      nw_act->remote_nick=NULL;

      /* remote side of the socket */
      nw_act->remote_addr=host_port;

      nw_act->run_task=NULL;
      nw_act->disp_info=NULL;

      /* remote client capabilities */
      nw_act->cap_str=NULL;
      nw_act->cap_ptr=NULL;

      /* create the new thread */
   pthread_attr_init (&thread_attr);
      pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);

   if(pthread_create(&(nw_act->thread_id),&thread_attr, (void*)connect_me_thread,nw_act)!=0)
      {
            int e=errno;

            pthread_attr_destroy(&thread_attr);

            if(e==EINTR)
            {
                  disp_msg(ERR_MSG,"do_connect_to_me","dctm: pthread_create failed",strerror(e),"MEMORY NOT FREED TO AVOID CRASH",NULL);
            }
            else
            {
                  disp_msg(ERR_MSG,"do_connect_to_me","dctm: pthread_create failed",strerror(e),NULL);
                  free(nw_act);
                  shutdown(sock_fd,2);
                  close(sock_fd);
                  return NULL;
            }
      }
      
      /* nw_act will be freed by the thread */
      pthread_attr_destroy(&thread_attr);

      /* we can end this thread now */
      pthread_exit(NULL);
}

/*******************************************************/
/* process the "$HubName " command                     */
/* this function is called when an user enters the hub */
/*******************************************************/
int hubname_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *t;
      int msg_type=(int)xtra_param;

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"hubname_process","no nick (2)",NULL);
            return 1;
      }

      (strchr(t,'|'))[0]='\0';                  /* never fails because DC line always ends with a | */

      disp_msg(msg_type,NULL,t,NULL);
      if(hubname==NULL)
            hubname=g_string_new(t);
      else
            hubname=g_string_assign(hubname,t);

      /* add the hubname (with its IP to recent) */
      {
            GString *recent;
            int fd;

            recent=g_string_new(dctc_dir->str);
            g_string_sprintfa(recent,"/recent");

            fd=open(recent->str,O_CREAT|O_RDWR,0666);
            if(fd==-1)
            {
                  perror("recent - open fails");
            }     
            else
            {
                  if(flock(fd,LOCK_EX)!=0)
                  {
                        perror("recent - lock fails");
                  }
                  else
                  {
                        lseek(fd,0,SEEK_END);
                        write(fd,org_hubip->str,org_hubip->len);        /* @@@ write on file */
                        write(fd,"|",1);                                                        /* @@@ write on file */
                        write(fd,hubname->str,hubname->len);                  /* @@@ write on file */
                        write(fd,"\n",1);                                                       /* @@@ write on file */
                        flock(fd,LOCK_UN);
                  }
                  close(fd);
            }
            
            g_string_free(recent,TRUE);
      }
      return 1;         /* end */
}

/*******************************************/
/* send the client capabilities to the hub */
/*******************************************/
void send_client2hub_capabilities(void)
{
      GString *str;
      int i;
      char *ptr;

      if(hub_logged!=1) /* and we must be logged */
            return;

      if((client_capabilities==NULL)||(client_capabilities->len==0))    /* and also have some capabilities */
            return;

      str=g_string_new(g_ptr_array_index(client_capabilities,0));
      i=1;
      while(i<client_capabilities->len)
      {
            ptr=g_ptr_array_index(client_capabilities,i);
            if(ptr!=NULL)
            {
                  g_string_append_c(str,'$');
                  g_string_append(str,ptr);
            }
            i++;
      }

      send_dc_line(main_sck,"$Capabilities",str->str,NULL);              /* get list of all nickname */
      g_string_free(str,TRUE);
}

/***********************************************************************/
/* in the string 'msg', replace all 'in' characters by 'out' character */
/***********************************************************************/
void subst_char(char *msg,char in, char out)
{
   while(*msg)
   {
      if(*msg==in)
         *msg=out;
      msg++;
   }
}

/*********************************************************/
/* process the "$NickList xxx$$[yyy$$zzz$$...]|" command */
/*********************************************************/
int nicklist_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *t;
      char *u;
      int msg_type=(int)xtra_param;
      void (*do_add)(char *nick);

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"nicklist_process","no nick list (2)",NULL);
            return 1;
      }

      /* according to the type of the message, we add to the user or to the op list */
      if(msg_type==USER_MSG)
            do_add=add_user_to_user_list;
      else
            do_add=add_user_to_op_list;

      while((*t)&&(*t!='|'))
      {
            u=strstr(t,"$$");
            if(u==NULL)
            {
                  disp_msg(ERR_MSG,"nicklist_process","invalid nick list",NULL);
                  return 1;
            }
      
            *u='\0';
            if(disp_user)
                  disp_msg(msg_type,NULL,t,NULL);
            (*do_add)(t);

            /* incoming user is always online */
            if(msg_type==OP_MSG)
                  uinfo_update_user_info(t,NULL,NULL,0,NULL,NULL,1,1);
            else
                  uinfo_update_user_info(t,NULL,NULL,0,NULL,NULL,0,1);
                  
            t=u+2;
      }
      return 1;         /* continue */
}

/*****************************************************************/
/* process the "$MyINFO $ALL xxx yyyy$ $zzzf$aaa$bbbb$" command  */
/* xxx= nickname                                                 */
/* yyy= file description (can be empty)                          */
/* zzz= connection type (Eg. DSL,Cable)                          */
/* f is a 1 byte flag. default value: 0x01                       */
/*         if the user is here, bit 1=1, else bit1=0 */
/* aaa=e-mail (can by empty)                                     */
/* bbb=size of shared data (in bytes)                            */
/*****************************************************************/
int myinfo_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *t;
      char *unick;
      char *udesc;
      char *utype;
      char *uemail;
      char *usize;
      char uflag;
      GString *uinfo;
      char cpy[2048];
      gchar **fields=NULL;
      int n;

      strncpy_max(cpy,input->str,sizeof(cpy));

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)
      if(*t=='\0')
      {
            invalid_data:
            if(fields!=NULL)
                  g_strfreev(fields);
            disp_msg(ERR_MSG,"myinfo_process","invalid myinfo data",cpy,NULL);
            return 1;
      }

      if(strncmp(t,"$ALL ",4))
      {
            disp_msg(ERR_MSG,"myinfo_process","no info type",t,cpy,NULL);
            return 1;
      }

      t+=4;
      SKIP_SPACE(t)
      if(*t=='\0')
            goto invalid_data;

      fields=g_strsplit(t,"$",9);   /* limit to 9 fields to avoid unnecessary splitting */
      n=gchar_array_len(fields);
      if(n==6)
      {
            /* fields[0]="xxx yyy" */
            /* fields[1]=" " */
            /* fields[2]="zzzf" */
            /* fields[3]="aaa" */
            /* fields[4]="bbbb" */
            /* fields[5]="" */
            /* extract nickname */
            assimilated_to_standard_myinfo:
            unick=fields[0];
            t=strchr(unick,' ');
            if(t==NULL)
                  goto invalid_data;
            *t++='\0';

            /* extract user description */
            SKIP_SPACE(t)
            udesc=t;

            /* extract connection type */
            utype=fields[2];
            n=strlen(utype);
            if(n<1)
                  goto invalid_data;
            uflag=utype[n-1];
            utype[n-1]='\0';

            /* extract e-mail */
            uemail=fields[3];

            /* extract data site */
            usize=fields[4];
      }
      else
      {
            if(n==5)
            {
                  /* some buggy clients have invalid myinfo */

                  /* some don't have the trailing '$' of the myinfo */
                  /* we check if the 2nd field is " " like nmdc */
                  if(!strcmp(fields[1]," "))
                        goto assimilated_to_standard_myinfo;
                  
                  /* some looks like "$MyINFO $ALL xxx T$zzzf$aaa$bbb$ */
                  /* where T can be empty or a " " */
                  disp_msg(ERR_MSG,"myinfo_process","malformed myinfo data. Trying to decode",cpy,NULL);

                  /* fields[0]="xxx T" */
                  /* fields[1]="zzzf" */
                  /* fields[2]="aaa" */
                  /* fields[3]="bbb" */
                  /* fields[4]="" */
                  unick=fields[0];
                  t=strchr(unick,' ');
                  if(t==NULL)
                        goto invalid_data;
                  *t++='\0';

                  /* extract user description */
                  SKIP_SPACE(t)
                  udesc=t;

                  /* extract connection type */
                  utype=fields[1];
                  n=strlen(utype);
                  if(n<1)
                        goto invalid_data;
                  uflag=utype[n-1];
                  utype[n-1]='\0';

                  /* extract e-mail */
                  uemail=fields[2];

                  /* extract data site */
                  usize=fields[4];
            }
            else
                  goto invalid_data;
      }


      uinfo=g_string_sized_new(1024);

      /* output format: nick size cnx_type 'email' udesc */
      {
            GString *uflag_txt;

            uflag_txt=g_string_new("");

            /* user is here ? */
            if(uflag&0x2)
                  uflag_txt=g_string_append(uflag_txt,"away");

            /* user is a server ? (more than 2 hours up) */
            if(uflag&0x4)
            {
                  if(uflag_txt->len)
                        uflag_txt=g_string_append_c(uflag_txt,',');
                  
                  uflag_txt=g_string_append(uflag_txt,"server");
            }

            /* user can do fast upload (more than 100KB/s) */
            if(uflag&0x8)
            {
                  if(uflag_txt->len)
                        uflag_txt=g_string_append_c(uflag_txt,',');
                  
                  uflag_txt=g_string_append(uflag_txt,"fast");
            }

            g_string_sprintf(uinfo,"%s %s %s [(%02X)%s] '%s' %s", unick,
                                                                                                strlen(usize)?usize:"0",
                                                                                                strlen(utype)?utype:"Unknown",
                                                                                                ((int)uflag)&255,
                                                                                                uflag_txt->str,
                                                                                                uemail,
                                                                                                udesc);
            g_string_free(uflag_txt,TRUE);
      }

      /* cache user info */
      set_cached_user_uinfo(unick,uinfo->str);

      /* set uinfo file for the user */
      uinfo_update_user_info( unick,
                                                      strlen(usize)?usize:"0",
                                                      strlen(utype)?utype:"Unknown",
                                                      uflag,uemail,udesc,-1,-1);

      if(disp_user)
            disp_msg(USER_INFO_MSG,NULL,uinfo->str,NULL);
      g_string_free(uinfo,TRUE);
      g_strfreev(fields);
      return 1;         /* end */
}

/*****************************************/
/* convert the given letter into boolean */
/*****************************************/
/* v='T' -> *result=1, return 0 */
/* v='F' -> *result=0, return 0 */
/* else return 1                */
/******************************************************************/
/* WARNING: don't inline this function. With gcc2.96, if inlined, */
/* search_process no more works                                   */
/******************************************************************/
int bool_letter(char v,int *result)
{
      if(v=='T')
      {
            *result=1;
            return 0;
      }

      if(v=='F')
      {
            *result=0;
            return 0;
      }

      return 1;
}

/*******************************************************/
/* process the "$GetPass" command                      */
/* this function is called when an user enters the hub */
/*******************************************************/
int getpass_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      if(nick_passwd==NULL)
      {
            disp_msg(GETPASS_MSG,NULL,NULL);
      }
      else
            send_dc_line(sck,"$MyPass",nick_passwd,NULL);

      return 1;         /* end */
}

/*******************************************************/
/* process the "$BadPass" command                      */
/* this function is called when an user enters the hub */
/*******************************************************/
int badpass_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      disp_msg(ERR_MSG,NULL,"Invalid password entered",NULL);

      force_quit=1;
      shutdown(sck,2);
      hub_disconnect(DO_EXIT);            /* never return because force_quit==1&&DO_EXIT */
      return 1;         /* end */
}

/***************************************************************/
/* process the "$LogedIn " command                             */
/* this function is called when the user is logged as an admin */
/***************************************************************/
int logedin_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *t;
      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"logedin_process","no nick (2)",NULL);
            return 1;
      }

      (strchr(t,'|'))[0]='\0';                  /* never fails because DC line always ends with a | */

      disp_msg(ADMIN_MSG,NULL,t,NULL);

      return 1;         /* end */
}

/*******************************************************/ 
/* process the "$ValidateDenied" command               */ 
/* this function is called when the nick is already    */ 
/* use on the hub                                      */ 
/*******************************************************/ 
int validate_denied_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      GString *str;

      LOCK_WRITE(user_info);
      str=g_string_new(nickname);
      str=g_string_append_c(str,'0'+rand()%10);
      free(nickname);
      nickname=str->str;
      g_string_free(str,FALSE);
      UNLOCK_WRITE(user_info);

      send_dc_line(sck,"$ValidateNick",nickname,NULL);
      return 1;
} 

GPtrArray *hub_capabilities=NULL;

/***************************************************************/
/* process the "$Capabilities" command                         */
/* this function is called when the hub sends its capabilities */
/***************************************************************/
int capabilities_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *s;
      char *t;

      wipe_GPtrArray_of_char_string(&hub_capabilities);
      hub_capabilities=g_ptr_array_new();

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)
      
      disp_msg(INFO_MSG,"capabilities_process","hub capabilities:",t,NULL);
      /* split capability string along '$' and put each keyword inside hub_capabilities array */
      while((*t!='|')&&(*t!='\0'))
      {
            char *tk;
            tk=strchr(t,'$');
            if(tk==NULL)
            {
                  tk=strchr(t,'|');
                  if(tk==NULL)
                        break;
            }

            *tk='\0';

            s=strdup(t);
            if(s!=NULL)
            {
                  g_ptr_array_add(hub_capabilities,s);
            }

            /* go to next command */
            t=tk+1;
      }

      /* send to the hub the client capabilities */
      /* send_dc_line(sck,"$Capabilities",CLIENT_CAPABILITY_LIST,NULL); */
      return 1;         /* end */
}

/********************************************************************************/
/* process the "$ForceMove " command                                            */
/* this function is called when a hub tries to reroute the client to another op */
/********************************************************************************/
int force_process(const char *cmd,int sck,GString *input, char *xtra_param)
{
      char *t;

      t=input->str+strlen(cmd);
      SKIP_SPACE(t)

      if(*t=='\0')
      {
            disp_msg(ERR_MSG,"force_process","no address (1)",NULL);
            return 1;
      }

      (strchr(t,'|'))[0]='\0';                  /* never fails because DC line always ends with a | */

      if(follow_force_move)
      {     
            /* we support the forcemove. This is done using the /GOTO command */
            GString *str;

            str=g_string_new("/GOTO ");
            str=g_string_append(str,t);
            add_new_sim_input(0,str->str);

            g_string_free(str,TRUE);
      }
      else
      {
            /* disconnect and reconnect later */
            hub_disconnect(DO_RECONNECT);
      }
      return 1;         /* end */
}

/***************************************************************************************/
/* check if the given message is a kick/ban message and add the IP into the UADDR list */
/***************************************************************************************/
void grab_ban_ip_fnc(GString *input)
{
      char *ip;
      unsigned int p1,p2,p3,p4;

      if(strncmp(input->str,"<Hub-Security> ",strlen("<Hub-Security> ")))
            return;

      ip=strstr(input->str," IP: ");
      if(ip==NULL)
            return;

      ip+=strlen(" IP: ");

      if(sscanf(ip,"%u.%u.%u.%u",&p1,&p2,&p3,&p4)==4)
      {
            char buf[512];

            sprintf(buf,"%u.%u.%u.%u:412",p1,p2,p3,p4);
            add_uaddr_entry_addr_dl_only(buf);
      }
}

/*******************************************************/
/* check if the given string has the following format: */
/* <Hub-Security> The user xxx was kicked by yyy       */
/*******************************************************/
/* output: 1=yes, 0=no */
/***********************/
int is_a_kick_message(char *str)
{
      if(strncmp(str,"<Hub-Security> The user ",strlen("<Hub-Security> The user ")))
            return 0;

      if(strstr(str+strlen("<Hub-Security> The user ")," was kicked by ")==NULL)
            return 0;

      return 1;
}

/***********************************************************************************************************/
/* check if the given public chat message is not emitted by a nickname having the "IGNORE_PUBMSG" flag set */
/***********************************************************************************************************/
/* output: 1= message is ok          */
/*         0= message is not allowed */
/*************************************/
int nick_does_not_have_ignore_pubmsg_flag(const GString *input)
{
      char *t;
      char *nick;
      int out;

      if(input->str[0]!='<')
            return 1;               /* no nickname at the beginning */

      t=strstr(input->str+1,"> ");
      if(t==NULL)
            return 1;               /* no end of nickname */

      nick=strdup(input->str+1);
      nick[t-(input->str+1)]='\0';        /* truncate the string just after the nickname */

      if(user_has_flag(nick,"IGNORE_PUBMSG"))
            out=0;
      else
            out=1;
      free(nick);
      return out;
}

/*************************************************/
/* get dc lines from network until nothing comes */
/**************************************************************************************/
/* all incoming lines are processed either to send new commands or to display results */
/**************************************************************************************/
void get_dc_line_and_process(int sck)
{
      GString *cur_input;
      static int call_counter=0;
      char tmp[512];

      sprintf(tmp,"call counter: %d",call_counter++);

      disp_msg(DEBUG_MSG,"get_dc_lines_until_no_more",tmp,NULL);

      while(1)
      {
            cur_input=get_a_dc_line(sck);
            if(cur_input==NULL)
            {
                  disp_msg(HUB_DISCONNECT,"","Hub has closed its connection.",NULL);
                  hub_disconnect(DO_RECONNECT);
                  return;
            }
      
            disp_msg(DEBUG_MSG,"get_dc_lines_until_no_more","processing ->",cur_input->str,NULL);
            if(process_incoming_dc_data(sck,cur_input))
                  break;

            g_string_free(cur_input,TRUE);
      }
      if(cur_input!=NULL)
            g_string_free(cur_input,TRUE);
}

/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* managment of com socket */
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */

/********************************************/
/* someone tries to connect to the com port */
/********************************************/
void manage_com_port(int in_sck)
{
      int sock_fd;
      WAIT_ACT *nw_act;
      pthread_attr_t thread_attr;
      struct sockaddr_in remote;
      int x=sizeof(remote);

      sock_fd=accept(in_sck,(struct sockaddr *)&remote,&x);
      if(sock_fd==-1)
      {
            disp_msg(ERR_MSG,"manage_com_port","someone fails to connect to com port",strerror(errno),NULL);
            return;
      }

      /* ok, remote host connect to the newly created socket */
      nw_act=malloc(sizeof(WAIT_ACT));
      if(nw_act==NULL)
      {
            disp_msg(ERR_MSG,"manage_com_port","malloc failed",NULL);
            shutdown(sock_fd,2);
            close(sock_fd);
            return;
      }

      disp_msg(DEBUG_MSG,"manage_com_port","creating thread info","|lu",(unsigned long)nw_act,NULL);
      nw_act->running=3;                  /* thread init */
      nw_act->cnx_type=CNX_ON_ACCEPT;
      nw_act->last_touch=time(NULL);
      nw_act->thread_id=0;
      nw_act->sock_fd=sock_fd;
      nw_act->remote_nick=NULL;

      /* remote address */
      nw_act->remote_addr=g_string_new("");
      g_string_sprintfa(nw_act->remote_addr,"%s:%hu", inet_ntoa(remote.sin_addr),ntohs(remote.sin_port));

      nw_act->run_task=NULL;
      nw_act->disp_info=NULL;

      /* remote client capabilities */
      nw_act->cap_str=NULL;
      nw_act->cap_ptr=NULL;

      /* create the new thread */
   pthread_attr_init (&thread_attr);
      pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
   if(pthread_create(&(nw_act->thread_id),&thread_attr, (void*)connect_me_thread,nw_act)!=0)
      {
            int e=errno;

            pthread_attr_destroy(&thread_attr);

            if(e==EINTR)
            {
                  disp_msg(ERR_MSG,"manage_com_port","mcp:pthread_create failed",strerror(e),"MEMORY NOT FREED TO AVOID CRASH",NULL);
            }
            else
            {
                  disp_msg(ERR_MSG,"manage_com_port","mcp:pthread_create failed",strerror(e),NULL);
                  free(nw_act);
                  shutdown(sock_fd,2);
                  close(sock_fd);
                  return;
            }
      }
      pthread_attr_destroy(&thread_attr);
      
      /* nw_act will be freed by the thread */
}


Generated by  Doxygen 1.6.0   Back to index