/* This file is part of Lcrzo.
*/

/* This file is part of Lcrzo.
*/

#ifdef LCRZODEF_SYSTEM_Unix
  /* this function is not needed*/

#elif defined LCRZODEF_SYSTEM_Windows

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_init_W95(lcrzo_priv_confvars *ptrcv,
                                           lcrzo_conststring keyname,
                                           lcrzo_bool canuseeth,
                                           lcrzo_bool canuseip);
int lcrzo_priv_conf_validdhcpinfo_init_W95(lcrzo_priv_confvars *ptrcv,
                                           lcrzo_conststring keyname,
                                           lcrzo_bool canuseeth,
                                           lcrzo_bool canuseip)
{ lcrzo_ipl gw;
  lcrzo_priv_conf_validdhcpinfo vdi;
  lcrzo_priv_conf_deviceeth de;
  lcrzo_data value;
  lcrzo_uint8 *pc, option;
	lcrzo_int32 optionsize, count, i, j, valuesize;
  lcrzo_bool yes;
  int ret;

	/* get info */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "DhcpInfo",
  						           &value, &valuesize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if (valuesize<=50)
  { lcrzo_data_free(value);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  /*lcrzo_data_print(value, valuesize, LCRZO_PRINTTYPE_DUMP);*/
  vdi.ip=(value[4]<<24)|(value[5]<<16)|(value[6]<<8)|value[7];
	vdi.mask=(value[8]<<24)|(value[9]<<16)|(value[10]<<8)|value[11];
  vdi.eth[0]=value[45];
  vdi.eth[1]=value[46];
  vdi.eth[2]=value[47];
  vdi.eth[3]=value[48];
  vdi.eth[4]=value[49];
  vdi.eth[5]=value[50];
  lcrzo_data_free(value);

  /* check IP */
  if (vdi.ip==0) return(LCRZO_ERR_OKUNRESOLVED);
  if (canuseip)
  { lcrzo_er(lcrzo_iplist_isinlist(&ptrcv->list_localip, vdi.ip, &yes));
    if (!yes) return(LCRZO_ERR_OKUNRESOLVED);
  }
  if (vdi.mask==0) return(LCRZO_ERR_OKUNRESOLVED);

  /* check hardware address */
  if (canuseeth)
  { lcrzo_er(lcrzo_list_count(&ptrcv->list_localeth, &count));
    yes=LCRZO_FALSE;
    for ( i=1 ; i<=count ; i++ )
    { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_localeth, i, &de));
      if ( lcrzo_etha_equal(de.eth, vdi.eth) )
      { lcrzo_device_init(de.device, vdi.device);
        yes=LCRZO_TRUE;
        break;
      }
    }
    if (!yes) return(LCRZO_ERR_OKUNRESOLVED);
  }

  /* set keyid */
  vdi.keyid[0]=keyname[strlen(keyname)-2];
  vdi.keyid[1]=keyname[strlen(keyname)-1];

  /* set routes (not compulsory) */
  vdi.gw[0]=0;
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "OptionInfo",
  						           &value, &valuesize);
  if (ret==LCRZO_ERR_OK)
  { /*lcrzo_data_print(value, valuesize, LCRZO_PRINTTYPE_DUMP);*/
		pc=value;
		j=0;
		while(pc<value+valuesize && ret==LCRZO_ERR_OK)
    { option=pc[0];
		  optionsize=pc[1];
      if (option==0xFF) break;
		  if (option==3 /*route*/)
      { 
        for ( i=0 ; i<optionsize/4 && ret==LCRZO_ERR_OK && j<LCRZO_PRIV_CONF_VALIDIPINFO_MAXGW-1 ; i++ )
        { gw=(pc[2+4*i]<<24)|(pc[3+4*i]<<16)|(pc[4+4*i]<<8)|pc[5+4*i];
          vdi.gw[j]=gw;
          j++;
        }
      }
      /* go to the next option */
		  pc+=optionsize+2;
		  /*lcrzo_data_print(pc, valuesize-(pc-value), LCRZO_PRINTTYPE_DUMP);*/
    }
	  vdi.gw[j]=0;
		lcrzo_data_free(value);
  }

  /* add to the chained list */
  lcrzo_er(lcrzo_list_add_last(&ptrcv->list_validdhcpinfo, &vdi));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_init_W98(lcrzo_priv_confvars *ptrcv,
                                           lcrzo_conststring keyname,
                                           lcrzo_bool canuseeth,
                                           lcrzo_bool canuseip);
int lcrzo_priv_conf_validdhcpinfo_init_W98(lcrzo_priv_confvars *ptrcv,
                                           lcrzo_conststring keyname,
                                           lcrzo_bool canuseeth,
                                           lcrzo_bool canuseip)
{ lcrzo_ipl gw;
  lcrzo_priv_conf_validdhcpinfo vdi;
  lcrzo_priv_conf_deviceeth de;
  lcrzo_data value;
  lcrzo_uint8 *pc, option;
	lcrzo_int32 optionsize, count, i, j, valuesize;
  lcrzo_bool yes;
  int ret;

  /* get IP */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "DhcpIPAddress",
  						           &value, &valuesize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if (valuesize!=LCRZO_IPA_MAXBYTES)
  { lcrzo_data_free(value);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  vdi.ip=(value[0]<<24)|(value[1]<<16)|(value[2]<<8)|value[3];
  lcrzo_data_free(value);
  if (vdi.ip==0) return(LCRZO_ERR_OKUNRESOLVED);
  if (canuseip)
  { lcrzo_er(lcrzo_iplist_isinlist(&ptrcv->list_localip, vdi.ip, &yes));
    if (!yes) return(LCRZO_ERR_OKUNRESOLVED);
  }

  /* get mask */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "DhcpSubnetMask",
  						           &value, &valuesize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if (valuesize!=LCRZO_IPA_MAXBYTES)
  { lcrzo_data_free(value);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  vdi.mask=(value[0]<<24)|(value[1]<<16)|(value[2]<<8)|value[3];
  lcrzo_data_free(value);
  if (vdi.mask==0) return(LCRZO_ERR_OKUNRESOLVED);

  /* get hardware type */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "HardwareType",
  						           &value, &valuesize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if ( valuesize!=1 || value[0]!=1 )
  { lcrzo_data_free(value);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  lcrzo_data_free(value);

  /* get hardware address */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "HardwareAddress",
  						           &value, &valuesize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if ( valuesize!=LCRZO_ETHA_MAXBYTES )
  { lcrzo_data_free(value);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  memcpy(vdi.eth, value, LCRZO_ETHA_MAXBYTES);
  lcrzo_device_initdefault(vdi.device);
  lcrzo_data_free(value);
  if (canuseeth)
  { lcrzo_er(lcrzo_list_count(&ptrcv->list_localeth, &count));
    yes=LCRZO_FALSE;
    for ( i=1 ; i<=count ; i++ )
    { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_localeth, i, &de));
      if ( lcrzo_etha_equal(de.eth, vdi.eth) )
      { lcrzo_device_init(de.device, vdi.device);
        yes=LCRZO_TRUE;
        break;
      }
    }
    if (!yes) return(LCRZO_ERR_OKUNRESOLVED);
  }

  /* set keyid */
  vdi.keyid[0]=keyname[strlen(keyname)-2];
  vdi.keyid[1]=keyname[strlen(keyname)-1];

  /* set routes (not compulsory) */
  vdi.gw[0]=0;
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, keyname, "OptionInfo",
  						           &value, &valuesize);
  if (ret==LCRZO_ERR_OK)
  { /*lcrzo_data_print(value, valuesize, LCRZO_PRINTTYPE_DUMP);*/
		pc=value;
		j=0;
		while(pc<value+valuesize && ret==LCRZO_ERR_OK)
    { option=pc[0];
		  optionsize=pc[1];
      if (option==0xFF) break;
		  if (option==3 /*route*/)
      { 
        for ( i=0 ; i<optionsize/4 && ret==LCRZO_ERR_OK && j<LCRZO_PRIV_CONF_VALIDIPINFO_MAXGW-1 ; i++ )
        { gw=(pc[2+4*i]<<24)|(pc[3+4*i]<<16)|(pc[4+4*i]<<8)|pc[5+4*i];
          vdi.gw[j]=gw;
          j++;
        }
      }
      /* go to the next option */
		  pc+=optionsize+2;
		  /*lcrzo_data_print(pc, valuesize-(pc-value), LCRZO_PRINTTYPE_DUMP);*/
    }
	  vdi.gw[j]=0;
		lcrzo_data_free(value);
  }

  /* add to the chained list */
  lcrzo_er(lcrzo_list_add_last(&ptrcv->list_validdhcpinfo, &vdi));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_init2(lcrzo_priv_confvars *ptrcv,
                                        lcrzo_uint32 version,
                                        lcrzo_bool canuseeth,
                                        lcrzo_bool canuseip);
int lcrzo_priv_conf_validdhcpinfo_init2(lcrzo_priv_confvars *ptrcv,
                                        lcrzo_uint32 version,
                                        lcrzo_bool canuseeth,
                                        lcrzo_bool canuseip)
{ lcrzo_string keyname;
  lcrzo_list list;
  lcrzo_int32 i, count;
  int ret;
  
  /* get the list of subkeys in DhcpInfo */
  ret=lcrzo_priv_winreg_subkeylist_get(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\VxD\\DHCP", &list);
  if (ret==LCRZO_ERR_OK)
  { ret=lcrzo_list_count(&list, &count);
    if (ret==LCRZO_ERR_OK)
    { 
      for ( i=1 ; i<=count && ret==LCRZO_ERR_OK ; i++ )
      { ret=lcrzo_datalist_valuem_pos(&list, i, NULL, (lcrzo_data*)&keyname, NULL);
        if (ret==LCRZO_ERR_OK)
        { /*printf("keyname=%s\n", keyname);*/
          /*printf("version=%d\n", version);*/
          if (version==1)
          { ret=lcrzo_priv_conf_validdhcpinfo_init_W95(ptrcv, keyname, canuseeth, canuseip);
          }
          else if (version==2)
          { ret=lcrzo_priv_conf_validdhcpinfo_init_W98(ptrcv, keyname, canuseeth, canuseip);
          }
          else
          { ret=lcrzo_priv_conf_validdhcpinfo_init_W98(ptrcv, keyname, canuseeth, canuseip);
            if ( ret!=LCRZO_ERR_OK && ret!=LCRZO_ERR_OKUNRESOLVED )
            { ret=lcrzo_priv_conf_validdhcpinfo_init_W95(ptrcv, keyname, canuseeth, canuseip);
            }
          }
          /* ignore bad entries */
          if (ret==LCRZO_ERR_OKUNRESOLVED) ret=LCRZO_ERR_OK;
          lcrzo_string_free(keyname);
        }
      }
    }
    lcrzo_list_close(&list);
  }

  return(ret);

}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_init(void);
int lcrzo_priv_conf_validdhcpinfo_init(void)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_uint32 version;
  lcrzo_data data;
  lcrzo_int32 datasize;
  lcrzo_bool canuseeth, canuseip;
  int ret;

  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));

  /* init ethernet */
  ret=lcrzo_priv_conf_localeth_eventuallyinit();
  if (ret==LCRZO_ERR_OK) 
  { canuseeth=LCRZO_TRUE;
  }
  else
  { canuseeth=LCRZO_FALSE;
  }

  /* init ip */
  ret=lcrzo_priv_conf_localip_eventuallyinit();
  if (ret==LCRZO_ERR_OK) 
  { canuseip=LCRZO_TRUE;
  }
  else
  { canuseip=LCRZO_FALSE;
  }

  /* get DhcpInfo version */
  version=3; /* default value */
  ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\VxD\\DHCP", "Version", &data, &datasize);
  if (ret==LCRZO_ERR_OK)
  { 
    if (datasize==4)
    { version= (data[3]<<24) | (data[2]<<16) | (data[1]<<8) | data[0];
    }
    lcrzo_data_free(data);
  }

  /* get conf */
  ret=lcrzo_priv_conf_validdhcpinfo_init2(ptrcv, version, canuseeth, canuseip);
  return(ret);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_eventuallyinit(void);
int lcrzo_priv_conf_validdhcpinfo_eventuallyinit(void)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_int32 count;

  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_count(&ptrcv->list_validdhcpinfo, &count));
  if (count==0)
  { lcrzo_er(lcrzo_priv_conf_validdhcpinfo_init());
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_get(lcrzo_constdevice device,
                                      lcrzo_int32 dhcpaddressnumber,
                                      lcrzo_priv_conf_validdhcpinfo *pvdi);
int lcrzo_priv_conf_validdhcpinfo_get(lcrzo_constdevice device,
                                      lcrzo_int32 dhcpaddressnumber,
                                      lcrzo_priv_conf_validdhcpinfo *pvdi)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_int32 i, count;
  int ret;

  ret=lcrzo_priv_conf_validdhcpinfo_eventuallyinit();
  if (ret!=LCRZO_ERR_OK) return(ret);

  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_count(&ptrcv->list_validdhcpinfo, &count));

  /* first, search the device */
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_validdhcpinfo, i, pvdi));
    if (lcrzo_device_equal(pvdi->device, device))
    { return(LCRZO_ERR_OK);
    }
  }

  /* now, access by number */
  lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_validdhcpinfo, dhcpaddressnumber, pvdi));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_debug_fprint(LCRZOFILE *pf);
int lcrzo_priv_conf_validdhcpinfo_debug_fprint(LCRZOFILE *pf)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_priv_conf_validdhcpinfo vdi;
  lcrzo_int32 i, count, j;

  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_count(&ptrcv->list_validdhcpinfo, &count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_validdhcpinfo, i, &vdi));
    fprintf(pf, "keyid=%c%c\n", vdi.keyid[0], vdi.keyid[1]);
    lcrzo_device_fprint(pf, " dev=", vdi.device, "\n");
    lcrzo_etha_fprint(pf, " eth=", vdi.eth, "\n");
    lcrzo_ipl_fprint(pf, " ip=", vdi.ip, "/");
    lcrzo_ipl_fprint(pf, "", vdi.mask, "\n");
    fprintf(pf, " gw=");
    for ( j=0 ; j<LCRZO_PRIV_CONF_VALIDIPINFO_MAXGW && vdi.gw[j]!=0 ; j++ )
    { lcrzo_ipl_fprint(pf, "", vdi.gw[j], ", ");
    }
    fprintf(pf, "\n");
  }

  return(LCRZO_ERR_OK);
}
/*-------------------------------------------------------------*/
int lcrzo_priv_conf_validdhcpinfo_init_debug_fprint(LCRZOFILE *pf);
int lcrzo_priv_conf_validdhcpinfo_init_debug_fprint(LCRZOFILE *pf)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_int32 i;
  lcrzo_bool j, k;

  fprintf(pf, "\n\nEntries in list_validdhcpinfo after lcrzo_priv_conf_validdhcpinfo_init\n");
  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_remove_all(&ptrcv->list_validdhcpinfo));
  lcrzo_err_fprint(pf, lcrzo_priv_conf_validdhcpinfo_init());
  lcrzo_er(lcrzo_priv_conf_validdhcpinfo_debug_fprint(pf));

  for ( i=0 ; i<3 ; i++ ) for ( j=0 ; j<=1 ; j++ ) for ( k=0 ; k<=1 ; k++ ) 
  { fprintf(pf, "\n\nEntries in list_validdhcpinfo after lcrzo_priv_conf_validdhcpinfo_init2\n");
    fprintf(pf, "version=%d, canuseeth=%d, canuseip=%d\n", i, j, k);
    lcrzo_er(lcrzo_list_remove_all(&ptrcv->list_validdhcpinfo));
    lcrzo_err_fprint(pf, lcrzo_priv_conf_validdhcpinfo_init2(ptrcv, i, j, k));
    lcrzo_er(lcrzo_priv_conf_validdhcpinfo_debug_fprint(pf));
  }

  return(LCRZO_ERR_OK);
}


#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif
