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

dc_com.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * dc_com.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_com.c,v 1.10 2003/12/28 08:12:38 uid68112 Exp $
*/

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <dirent.h>
#include <errno.h>
#include <glib.h>

#include "dc_com.h"
#include "display.h"
#include "main.h"
#include "var.h"
#include "misc.h"

char last_cmd[5120];                /* contains the first string of the last send_dc_line call */
                                                      /* mainly used for debug features.                         */
time_t last_cmd_time;

/****************************************************/
/* write the given strings on the socket            */
/* a space will be written between each string      */
/* a | will be added at the end                     */
/*************************************************************/
/* if connection is not yet established (cnx_in_progress!=0) */
/* the data to send are discarded, no error is returned      */
/*************************************************************/
/* to avoid network problem, we use send instead of write, */
/* cf. get_a_dc_line comment to understand why             */
/***********************************************************/
void send_dc_line(int sck,...)
{
      va_list ap;
      char *t;
      int have=0;
      GString *str;

      if(sck==-1)       /* sometimes, after a network error, it is possible to come here with an invalid socket descriptor */
            return;

      str=g_string_new(NULL);
      va_start(ap,sck);
      t=va_arg(ap,char *);

      strncpy_max(last_cmd,t,sizeof(last_cmd));

      while(t!=NULL)
      {
            if(have!=0)
            {     /* add a space between fields */
                  str=g_string_append_c(str,' ');
            }
            have=1;

            str=g_string_append(str,t);

            t=va_arg(ap,char *);
      }

      if(have==1)
      {
            str=g_string_append_c(str,'|');
      }

      if((str->len)&&(cnx_in_progress==0))
      {
            if(send(sck,str->str,str->len,MSG_NOSIGNAL)!=str->len)
            {
                  /* abort network operation on this socket */
                  /* this will either generated a hub_disconnection message (main thread) */
                  /* or leave xfer threads on network error. */
                  shutdown(sck,2);
            }
            last_cmd_time=time(NULL);
      }

      disp_msg(DEBUG_MSG,"send_dc_line","Sent",str->str,NULL);

      g_string_free(str,TRUE);
      va_end(ap);
}

/**************************************/
/* get a DC line (always ends by a |) */
/************************************************************************/
/* theory: DC client used send (not write) to send data over network    */
/*         so there is (invisible) message boundary inside network data */
/*         Thus, if you use recv to obtain data, we are sure to obtain  */
/*         a line ending by a |. However, we can receive more than one  */
/*         line at the same time.                                       */
/* real: to avoid buffer overflow and to be sure to have exactly one    */
/*       line, we read byte by byte network data. It is less efficient  */
/*       but it is safer and who cares about efficency to get 50 bytes. */
/*******************************************************************************/
/* input: sck= socket to read from                                             */
/* output: NULL or a GString (must be freed when no more used) ending with a | */
/*******************************************************************************/
GString *get_a_dc_line(int sck)
{
      GString *s;
      int n;
      char c;
      fd_set inp;
      struct timeval tv;
      int retval;


      if(sck==-1)       /* sometimes, after a network error, it is possible to come here with an invalid socket descriptor */
            return NULL;

      s=g_string_sized_new(512);

      do
      {
            FD_ZERO(&inp);
            FD_SET(sck,&inp);
            tv.tv_sec=30;
            tv.tv_usec=0;

            retval=select(sck+1,&inp,NULL,NULL,&tv);
            if((retval==-1)&&(errno==EINTR))
                  continue;
            if((retval==-1)||(!FD_ISSET(sck,&inp)))
            {
                  perror("get_a_dc_line - select");
#if 0
                  printf("%s\n",s->str);
#endif
                  g_string_free(s,TRUE);
                  return NULL;
            }
      
            n=read(sck,&c,1);
            if(n!=1)
            {
#if 0
                  printf("%s\n",s->str);
#endif
                  g_string_free(s,TRUE);
                  return NULL;
            }

            s=g_string_append_c(s,c);
      }while(c!='|');

      disp_msg(DEBUG_MSG,"get_a_dc_line","Received",s->str,NULL);
      return s;
}

/* contains pointers on the path (stored in dctc_gsc) of all '.udp' files of other DCTC */
static GPtrArray *running_dctc=NULL;
/* this array contains the paths themselves */
static GStringChunk *dctc_gsc=NULL;

/*****************************************************************/
/* get the number of running DCTC (without counting this client) */
/*****************************************************************/
int num_running_dctc(void)
{
      int delta=0;
      int i;

      if(running_dctc==NULL)
      {
            refresh_known_client_list(TRUE);
            if(running_dctc==NULL)
                  return 1;
      }

      /* adjust the truly running client byt removing the dummy_client if it exists */
      for(i=0;i<running_dctc->len;i++)
      {
            const char *a;
            int ln;

            a=g_ptr_array_index(running_dctc,i);
            ln=strlen(a);

            if(ln>=strlen("-dummy_client.udp"))
            {
                  if(!strcmp(a+ln-strlen("-dummy_client.udp"),"-dummy_client.udp"))
                  {
                        delta=-1;
                        break;
                  }
            }
      }

      return (running_dctc->len) + delta +1;          /* -1 because the dummy_client is counted as a running client but it is never connected to anything */
                                                                                    /* but +1 because the list does not contained the current client */
}

/*********************************************/
/* refresh the list of known running clients */
/***************************************************/
/* if force==TRUE, the refresh is forced, else     */
/* a delay of at least 60 seconds will be observed */
/***************************************************/
void refresh_known_client_list(gboolean force)
{
      static time_t last_update=0;
      time_t cur_time;
      static GString *running_dir=NULL;
      DIR *dir;

      cur_time=time(NULL);
      if(((cur_time-last_update)>60)||(force==TRUE))
      {
            /* refresh the running dctc list every 60 seconds */
            last_update=cur_time;

            if(running_dctc!=NULL)
                  g_ptr_array_free(running_dctc,TRUE);
            running_dctc=g_ptr_array_new();

            if(dctc_gsc!=NULL)
                  g_string_chunk_free(dctc_gsc);
            dctc_gsc=g_string_chunk_new(128);

            if(running_dir==NULL)
            {
                  /* create a string with "$HOME/.dctc/running/" */
                  char *path;

                  path=getenv("HOME");
                  running_dir=g_string_new((path!=NULL)?path:".");
                  running_dir=g_string_append(running_dir,"/.dctc/running/");
            }

            /* fill the running_dctc array with the path to UDP socket of all running dctc */
            dir=opendir(running_dir->str);
            if(dir!=NULL)
            {
                  struct dirent *obj;
                  GString *np;

                  np=g_string_new("");

                  while((obj=readdir(dir))!=NULL)
                  {
                        /* check if the filename is like "dctc-*.udp" */
                        if(obj->d_name[0]=='.')
                              continue;
                        if(strncmp(obj->d_name,"dctc-",5))
                              continue;
                        if(strcmp(obj->d_name+strlen(obj->d_name)-4,".udp"))
                              continue;

                        g_string_sprintf(np,"%s%s",running_dir->str,obj->d_name);

                        /* don't have our local udp socket to the list */
                        if((local_dctc_udp_sock_path) && (strcmp(np->str,local_dctc_udp_sock_path->str)))
                              g_ptr_array_add(running_dctc,g_string_chunk_insert(dctc_gsc,np->str));
                  }

                  g_string_free(np,TRUE);
                  closedir(dir);
            }
      }
}

/* ------------------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------------------- */
static void send_str_to_dctc_link(GString *str)
{

      refresh_known_client_list(FALSE);

      if((running_dctc!=NULL)&&(running_dctc->len!=0))
      {
            int i;

            for(i=0;i<running_dctc->len;i++)
            {
                  char *fname;

                  fname=g_ptr_array_index(running_dctc,i);
                  if(fname!=NULL)
                  {
                        struct sockaddr_un name;
                        name.sun_family=AF_UNIX;
                        strcpy(name.sun_path,fname);

                        if(sendto(local_sck_udp,str->str,str->len,MSG_NOSIGNAL|MSG_DONTWAIT,(void*)&name,sizeof(struct sockaddr_un))!=str->len)
                        {
                              disp_msg(DEBUG_MSG,"send_str_to_dctc_link","sendto fail",strerror(errno),NULL);
                        }
                  }
            }
      }
}

/**********************************************************************/
/* this function works like send_dc_line except that the built string */
/* is sent to all other running client                                */
/**********************************************************************/
/* an optionnal condition can be added to the sent command */
/***********************************************************/
void send_dc_line_to_dctc_link(char *condition,int sck,...)
{
      va_list ap;
      char *t;
      int have=0;
      GString *str;

#if 0
      if(sck==-1)       /* sometimes, after a network error, it is possible to come here with an invalid socket descriptor */
            return;
#endif

      /* prepend the condition if it exists */
      if(condition==NULL)
      {
            str=g_string_new(NULL);
      }
      else
      {
            str=g_string_new("~");
            str=g_string_append(str,condition);
            str=g_string_append_c(str,'|');
      }

      va_start(ap,sck);
      t=va_arg(ap,char *);

      strncpy_max(last_cmd,t,sizeof(last_cmd));

      while(t!=NULL)
      {
            if(have!=0)
            {     /* add a space between fields */
                  str=g_string_append_c(str,' ');
            }
            have=1;

            str=g_string_append(str,t);

            t=va_arg(ap,char *);
      }

      if(have==1)
      {
            str=g_string_append_c(str,'|');
      }

      /* add a \n at the end because dctc_link wants one */
      str=g_string_append_c(str,'\n');

      if(str->len)
      {
            send_str_to_dctc_link(str);
      }

      disp_msg(DEBUG_MSG,"send_dc_line_to_dctc_link","Sent",str->str,NULL);

      g_string_free(str,TRUE);
      va_end(ap);
}

/********************************************************************************************/
/* this function works like send_dc_line_to_dctc_link except that it takes the given string */
/* as is append a \n and send it to all other running clients                               */
/********************************************************************************************/
/* an optionnal condition can be added to the sent command */
/***********************************************************/
void send_dc_line_to_dctc_link_direct(char *condition,char *cmd)
{
      GString *str;

      /* prepend the condition if it exists */
      if(condition==NULL)
      {
            str=g_string_new(NULL);
      }
      else
      {
            str=g_string_new("~");
            str=g_string_append(str,condition);
            str=g_string_append_c(str,'|');
      }

      str=g_string_append(str,cmd);

      /* add a \n at the end because dctc_link wants one */
      str=g_string_append_c(str,'\n');

      if(str->len)
      {
            send_str_to_dctc_link(str);
      }

      disp_msg(DEBUG_MSG,"send_dc_line_to_dctc_link_direct","Sent",str->str,NULL);

      g_string_free(str,TRUE);
}


/**************************************/
/* send the given string to the UNODE */
/**************************************/
void send_str_to_unode(GString *str)
{
      static GString *unode_fname=NULL;
      struct sockaddr_un name;

      if(unode_fname==NULL)
      {
            unode_fname=g_string_new(dctc_dir->str);
            unode_fname=g_string_append(unode_fname,"/dctc-unode.udp");
      }

      name.sun_family=AF_UNIX;
      strcpy(name.sun_path,unode_fname->str);

      if(sendto(local_sck_udp,str->str,str->len,MSG_NOSIGNAL|MSG_DONTWAIT,(void*)&name,sizeof(struct sockaddr_un))!=str->len)
      {
            disp_msg(ERR_MSG,"send_str_to_unode","sendto fail",strerror(errno),NULL);
      }
}



Generated by  Doxygen 1.6.0   Back to index