/*
		                  LCRZO
                             Network library
             Copyright(c) 1999,2000,2001,2002 Laurent Constantin
                                  -----

  Main server    : http://www.laurentconstantin.com/
  Backup servers : http://go.to/laurentconstantin/
                   http://laurentconstantin.est-la.com/
                   http://laurentconstantin.free.fr/
                   http://lauconstantin.multimania.com/
  [my current email address is on the web servers]

                                  -----
  This file is part of Lcrzo.

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

  Lcrzo 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
  Lesser General Public License for more details (http://www.gnu.org).

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

/* -- Note about comments and variable names --
   In versions 1.00 up to 2.08, lcrzo was only written in French (my
   mother tongue). Since version 3.00, lcrzo is written in English, but
   most of comments and variable names are still in French.
   In future versions, I'll try to translate everything into English,
   but as you may guess, that's a quite big work...
   So, for the moment, here are some word translation, if you want to
   be able to understand what's going on in the functions :
     afficher - print     ;  ajouter - add         ;  copie - copy
     dernier - last       ;  donnee - data         ;  ecrire - write
     entete - header      ;  envoye - sent         ;  ferme - close
     fichier - file       ;  lire - read           ;  masque - mask 
     nboct - byte number  ;  ouvre, ouvrir - open  ;  paq, paquet - packet
     plage - range        ;  premier - first       ;  recu - received
     reseau - network     ;  retour - return       ;  taille - size
     tunnel - pipe

*/

#include "lcrzo_incl.h"


/*-------------------------------------------------------------*/
int lcrzo_device_init(const char *device_src, lcrzo_device device_dest)
{ lcrzo_er(lcrzo_string_init_coretext(device_src, LCRZO_DEVICE_MAXBYTES, 
				      device_dest));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_device_init_noalias(const char *device_src, lcrzo_device device_dest)
{ lcrzo_string tmp;
  char *pc;
  int ret;

  lcrzo_er(lcrzo_string_initm_text(device_src, &tmp));
  pc=strchr(tmp, ':');
  if ( pc!=NULL ) *pc='\0';

  ret=lcrzo_string_init_coretext(tmp, LCRZO_DEVICE_MAXBYTES, device_dest);
  lcrzo_string_free(tmp);
  return(ret);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_device_equal(const lcrzo_device device1, const lcrzo_device device2)
{ lcrzo_device dev1, dev2;
  int retour;

  retour=lcrzo_string_init_coretext(device1, LCRZO_DEVICE_MAXBYTES, dev1);
  if (retour!=LCRZO_ERR_OK) return(LCRZO_FALSE);
  retour=lcrzo_string_init_coretext(device2, LCRZO_DEVICE_MAXBYTES, dev2);
  if (retour!=LCRZO_ERR_OK) return(LCRZO_FALSE);
  if (!strcmp(dev1, dev2)) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_int8 lcrzo_device_cmp(const lcrzo_device device1, const lcrzo_device device2)
{ lcrzo_device dev1, dev2;
  int retour;

  retour=lcrzo_string_init_coretext(device1, LCRZO_DEVICE_MAXBYTES, dev1);
  if (retour!=LCRZO_ERR_OK) return(2);
  retour=lcrzo_string_init_coretext(device2, LCRZO_DEVICE_MAXBYTES, dev2);
  if (retour!=LCRZO_ERR_OK) return(2);

  retour=strcmp(dev1, dev2);
  if (retour<0) return(-1);
  else if (retour>0) return(+1);
  else return(0);
}

/*-------------------------------------------------------------*/
int lcrzo_device_initdefault(lcrzo_device device)
{ if (device!=NULL) device[0]='\0';
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_mtu_init_device(const lcrzo_device device,
			  lcrzo_uint32 *pmtu)
{ lcrzo_int32 count, i;
  lcrzo_device founddevice;
  lcrzo_uint32 mtu, mtumax;

  /*verification du parametre*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);

  /* obtain the number of local devices */
  lcrzo_er(lcrzo_conf_devices_count(&count));

  /* the "any" device is particular */
  if ( lcrzo_device_equal(device, "any") )
  { mtumax=0;
    for ( i=1 ; i<=count ; i++ )
    { lcrzo_er(lcrzo_conf_devices_value_pos(i, NULL, NULL, NULL, NULL,
					    &mtu, NULL, NULL));
      if (mtu>mtumax) mtumax=mtu;
    }
    if (pmtu!=NULL) *pmtu=mtumax;
    return(LCRZO_ERR_OK);
  } 

  /* get the mtu of other devices */
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_conf_devices_value_pos(i, founddevice, NULL, NULL, NULL,
					  pmtu, NULL, NULL));
    if ( lcrzo_device_equal(founddevice, device) )
    { return(LCRZO_ERR_OK);
    }
  } 
  /* unknown device */
  return(LCRZO_ERR_SPUNKNOWNDEVICE);
}

/*-------------------------------------------------------------*/
int lcrzo_etha_init_device(const lcrzo_device device,
			   lcrzo_etha etha)
{ lcrzo_int32 count, i;
  lcrzo_device founddevice;

  /*verification du parametre*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  /* search the device */
  lcrzo_er(lcrzo_conf_devices_count(&count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_conf_devices_value_pos(i, founddevice, etha, NULL, NULL,
					  NULL, NULL, NULL));
    if ( lcrzo_device_equal(founddevice, device) )
    { return(LCRZO_ERR_OK);
    }
  } 
  /* unknown device */
  return(LCRZO_ERR_SPUNKNOWNDEVICE);
}

/*-------------------------------------------------------------*/
int lcrzo_ipl_init_device(const lcrzo_device device,
			  lcrzo_ipl *pipl,
			  lcrzo_ipl *pmask)
{ lcrzo_int32 count, i;
  lcrzo_device founddevice;

  /*verification du parametre*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);

  /* the "any" device is particular */
  if ( lcrzo_device_equal(device, "any") )
  { if (pipl!=NULL) *pipl=0;
    if (pmask!=NULL) *pmask=0;
    return(LCRZO_ERR_OK);
  } 

  /* search the device */
  lcrzo_er(lcrzo_conf_devices_count(&count));
  for ( i=1 ; i<=count ; i++ )
    { lcrzo_er(lcrzo_conf_devices_value_pos(i, founddevice, NULL, pipl, pmask,
					    NULL, NULL, NULL));
    if ( lcrzo_device_equal(founddevice, device) )
    { return(LCRZO_ERR_OK);
    }
  } 
  /* unknown device */
  return(LCRZO_ERR_SPUNKNOWNDEVICE);
}

/*-------------------------------------------------------------*/
int lcrzo_hs_init_device(const lcrzo_device device,
			 lcrzo_hs hs)
{ lcrzo_ipl ipl;

  lcrzo_er(lcrzo_ipl_init_device(device, &ipl, NULL));
  lcrzo_er(lcrzo_hs_init_ipl(ipl, hs));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_device_veriftype(lcrzo_int16 type);
int lcrzo_priv_device_veriftype(lcrzo_int16 type)
{ lcrzo_int16 language;
  switch (type)
  { case LCRZO_DEVICE_TYPE_ALL:
    case LCRZO_DEVICE_TYPE_ONLYVALIDIP:
    case LCRZO_DEVICE_TYPE_ONLYBOARDS:
    case LCRZO_DEVICE_TYPE_LOOPBACK:
    case LCRZO_DEVICE_TYPE_UP:
    case LCRZO_DEVICE_TYPE_ALIAS:
      break;
    default:
      lcrzo_er(lcrzo_global_get_language(&language));
      if (language==LCRZO_GLOBAL_LANG_FR)
	lcrzo_priv_global_set_errmsglcrzo("device type is not ALL, ONLYVALIDIP, ONLYBOARDS, LOOPBACK, UP, ALIAS");
      else                         
	lcrzo_priv_global_set_errmsglcrzo("le type du device n'est pas ALL, ONLYVALIDIP, ONLYBOARDS, LOOPBACK, UP, ALIAS");
      return(LCRZO_ERR_PAUNKNOWNTYPE);
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_device_istype(const lcrzo_device device,
			lcrzo_int16 type,
			lcrzo_bool *pyesitistype)
{ lcrzo_device founddevice, searcheddevice;
  lcrzo_bool yes, isup, isanalias;
  lcrzo_int32 count, i;
  lcrzo_ipl ipl;

  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  lcrzo_er(lcrzo_device_init(device, searcheddevice));
  lcrzo_er(lcrzo_priv_device_veriftype(type));

  /* just because of the compiler warning */
  isanalias=LCRZO_FALSE;
  isup=LCRZO_FALSE;
  ipl=0;

  /* search the device */
  lcrzo_er(lcrzo_conf_devices_count(&count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_conf_devices_value_pos(i, founddevice, NULL, &ipl, NULL,
					  NULL, &isup, &isanalias));
    if ( lcrzo_device_equal(founddevice, searcheddevice) ) break;
  } 
  if (i>count) return(LCRZO_ERR_SPUNKNOWNDEVICE);

  /*now, we test*/
  yes=0;
  switch (type)
  { case LCRZO_DEVICE_TYPE_ALL:
      /*loopback, down interfaces, up interfaces, down aliases, up aliases*/
      yes=1;
      break;
    case LCRZO_DEVICE_TYPE_ONLYVALIDIP:
      /*up interfaces, up aliases*/
      if ( isup && ipl!=0x7F000001 )
      { yes=1;
      }
      break;
    case LCRZO_DEVICE_TYPE_ONLYBOARDS:
      if ( isup && !isanalias && ipl!=0x7F000001 )
      { yes=1;
      }
      break;
    case LCRZO_DEVICE_TYPE_LOOPBACK:
      /*loopback*/
      if ( ipl==0x7F000001 )
      { yes=1;
      }
      break;
    case LCRZO_DEVICE_TYPE_UP:
      /*up interfaces*/
      if ( isup )
      { yes=1;
      }
      break;
    case LCRZO_DEVICE_TYPE_ALIAS:
      if ( isanalias )
      { yes=1;
      }
      break;
  }

  if (pyesitistype!=NULL) *pyesitistype=yes;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_device_count(lcrzo_int16 type,
		       lcrzo_int32 *pcount)
{ lcrzo_bool isup, isanalias;
  lcrzo_int32 count, i, countall, countvalidip, countonlyboards, countloopback;
  lcrzo_int32 countup, countalias;
  lcrzo_ipl ipl;

  /*parameters verification*/
  lcrzo_er(lcrzo_priv_device_veriftype(type));

  /* search the device */
  countall=0;
  countvalidip=0;
  countonlyboards=0;
  countloopback=0;
  countup=0;
  countalias=0;
  lcrzo_er(lcrzo_conf_devices_count(&count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_conf_devices_value_pos(i, NULL, NULL, &ipl, NULL,
					  NULL, &isup, &isanalias));
    countall++;
    if ( isup && ipl!=0x7F000001 )
    { countvalidip++;
      if ( !isanalias )
      { countonlyboards++;
      }
    }
    if ( ipl==0x7F000001 )
    { countloopback++;
    }
    if ( isup )
    { countup++;
    }
    if ( isanalias )
    { countalias++;
    }
  } 

  if ( pcount!=NULL )
  { switch (type)
    { case LCRZO_DEVICE_TYPE_ALL:
        *pcount=countall;
        break;
      case LCRZO_DEVICE_TYPE_ONLYVALIDIP:
        *pcount=countvalidip;
        break;
      case LCRZO_DEVICE_TYPE_ONLYBOARDS:
        *pcount=countonlyboards;
        break;
      case LCRZO_DEVICE_TYPE_LOOPBACK:
        *pcount=countloopback;
        break;
      case LCRZO_DEVICE_TYPE_UP:
        *pcount=countup;
        break;
      case LCRZO_DEVICE_TYPE_ALIAS:
        *pcount=countalias;
        break;
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_device_value_pos(lcrzo_int16 type,
			   lcrzo_int32 pos,
			   lcrzo_device device)
{ lcrzo_int32 pospos, currentpos;
  lcrzo_bool isup, isanalias;
  lcrzo_int32 count, i;
  lcrzo_ipl ipl;

  /*verification des parametres et affectation des positions*/
  lcrzo_er(lcrzo_device_count(type, &count));
  lcrzo_er(lcrzo_priv_manageelempos(count, pos, 0, NULL, &pospos, NULL,NULL));

  /* search the device */
  currentpos=0;
  lcrzo_er(lcrzo_conf_devices_count(&count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_conf_devices_value_pos(i, device, NULL, &ipl, NULL,
					  NULL, &isup, &isanalias));
    switch (type)
    { case LCRZO_DEVICE_TYPE_ALL:
        currentpos++;
        break;
      case LCRZO_DEVICE_TYPE_ONLYVALIDIP:
        if ( isup && ipl!=0x7F000001 ) currentpos++;
        break;
      case LCRZO_DEVICE_TYPE_ONLYBOARDS:
        if ( isup && !isanalias && ipl!=0x7F000001 ) currentpos++;
        break;
      case LCRZO_DEVICE_TYPE_LOOPBACK:
        if ( ipl==0x7F000001 ) currentpos++;
        break;
      case LCRZO_DEVICE_TYPE_UP:
        if ( isup ) currentpos++;
        break;
      case LCRZO_DEVICE_TYPE_ALIAS:
        if ( isanalias ) currentpos++;
        break;
      default:
        return(lcrzo_ieuv("lcrzo_device_value_pos", "type"));
    }
    if ( currentpos==pospos ) return(LCRZO_ERR_OK);
  }

  return(lcrzo_ie("lcrzo_device_value_pos",
		  "la valeur de pos aurait due etre verifiee",
		  "value of pos should have been checked"));
}

/*-------------------------------------------------------------*/
int lcrzo_device_value_first(lcrzo_int16 type,
			     lcrzo_device device)
{ return(lcrzo_device_value_pos(type, +1, device));
}

/*-------------------------------------------------------------*/
int lcrzo_device_value_last(lcrzo_int16 type,
			    lcrzo_device device)
{ return(lcrzo_device_value_pos(type, -1, device));
}

/*-------------------------------------------------------------*/
int lcrzo_device_loop_range(lcrzo_int16 type,
			    lcrzo_int32 startpos,
			    lcrzo_int32 endpos,
			    int (*pfonc)(lcrzo_device device,
					 void *pinfos),
			    void *pinfos)
{ lcrzo_int32 count, suppos, infpos, i;
  lcrzo_device device;

  /*parameters verification*/
  if (pfonc==NULL) return(LCRZO_ERR_PANULLPTR);
  lcrzo_er(lcrzo_device_count(type, &count));
  if ( count==0 && startpos==+1 && endpos==-1 )
    return(LCRZO_ERR_OK);
  lcrzo_er(lcrzo_priv_manageelemrange(count, startpos, endpos,
				      NULL, &infpos, NULL, &suppos,
				      NULL,NULL,NULL,NULL));

  if (suppos>=infpos)
  { for ( i=infpos ; i<=suppos ; i++ )
    { lcrzo_er(lcrzo_device_value_pos(type, i, device));
      lcrzo_er(((*pfonc)(device, pinfos)));
    }
  }
  else
  { for ( i=infpos ; i>=suppos ; i-- )
    { lcrzo_er(lcrzo_device_value_pos(type, i, device));
      lcrzo_er(((*pfonc)(device, pinfos)));
    }
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_device_loop_all(lcrzo_int16 type,
			  int (*pfonc)(lcrzo_device device,
				       void *pinfos),
			  void *pinfos)
{ return(lcrzo_device_loop_range(type, +1, -1, pfonc, pinfos));
}

/*-------------------------------------------------------------*/
int lcrzo_device_init_stdin(const char *message,
			    lcrzo_constdevice devicedefaut, 
			    lcrzo_device device)
{ lcrzo_bool defaultdevicefound;
  lcrzo_uint32 devicenumber, defaultdevicenumber;
  lcrzo_int32 count, i;
  lcrzo_device dev;
  lcrzo_string usertext;
  char prompt;
  lcrzo_int16 language;

  /* initialization */
  lcrzo_er(lcrzo_device_count(LCRZO_DEVICE_TYPE_ALL, &count));
  defaultdevicefound=LCRZO_FALSE;
  defaultdevicenumber=0;

  if (message!=NULL) if (strlen(message)) printf("%s\n", message);
  /* display possible choices */
  for ( i=1 ; i<=count ; i++ )
  { /* display */
    lcrzo_int32_print(" ", i, "%2d", " - ");
    lcrzo_device_value_pos(LCRZO_DEVICE_TYPE_ALL, i, dev);
    lcrzo_device_print("", dev, "\n");
    /* default device found*/
    if (devicedefaut!=NULL) 
    { if ( lcrzo_device_equal(devicedefaut, dev) )
      { defaultdevicefound=LCRZO_TRUE;
        defaultdevicenumber=i;
      }
    }
  }
  lcrzo_er(lcrzo_global_get_language(&language));
  count++;
  lcrzo_int32_print(" ", count, "%2d", " - ");
  printf("%s\n",
	 (language==LCRZO_GLOBAL_LANG_FR)?"Autre device":"Other device");

  /* if the default value was not found */
  if (!defaultdevicefound)
  { defaultdevicenumber=count;
  }

  /* ask user */ 
  lcrzo_er(lcrzo_uint32_init_stdin((language==LCRZO_GLOBAL_LANG_FR)?
				   "Entrez votre choix":"Enter your choice", 
				   1, count, defaultdevicenumber,
				   &devicenumber));
  if (devicenumber!=(lcrzo_uint32)count)
  { lcrzo_er(lcrzo_device_value_pos(LCRZO_DEVICE_TYPE_ALL, 
				    devicenumber, device));
  }
  else
  { prompt=':';
    while(LCRZO_TRUE)
    { /*affichage du message*/
      if (message!=NULL) if (message[0]!='\0')
      { printf("%s ", message);
        if (!defaultdevicefound && devicedefaut!=NULL )
        { if (strlen(devicedefaut)) printf("[%s]", devicedefaut);
	}
        printf("%c ", prompt);
      }
      prompt='>';
      /*saisie de la chaine de l'utilisateur*/
      lcrzo_er(lcrzo_priv_stdin(&usertext, NULL));
      /* exit if something was entered */
      if (strlen(usertext)!=0)
      { lcrzo_er(lcrzo_device_init(usertext, device));
        lcrzo_string_free(usertext);
        break;
      }
      else
      { if (!defaultdevicefound && devicedefaut!=NULL)
        { if (strlen(devicedefaut))
          { lcrzo_string_free(usertext);
            lcrzo_er(lcrzo_device_init(devicedefaut, device));
	    break;
	  }
        }
      }
      lcrzo_string_free(usertext);
    }
  }
  return(LCRZO_ERR_OK);
}
