/**************************************************
OpengateM - a MAC address authentication system
 module for getting parameters from conf file

configuration file has XML form which is parsed by ezxml liblary.
functions relating Extraset and authserver are not used in daemon program.

Copyright (C) 2006 Opengate Project Team
Written by Yoshiaki Watanabe


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.

Email: watanaby@is.saga-u.ac.jp

Programmed by Yoshiaki WATANABE

**************************************************/
#include "opengatemd.h"
#include "../ezxml/ezxml.h"

#define CONFIG_VERSION "0.6.0"  /* should match to the value in conf file */
#define SEPARATOR "/"

int debug=0;
static ezxml_t xmlRoot=NULL;
static ezxml_t xmlExtraSet=NULL;
static ezxml_t xmlAuthServer=NULL;
static ezxml_t xml=NULL;
static ezxml_t xmlSave=NULL;

char *getConfValueExtra(char *name);
char *getConfValue(char *name);
char *convertToFacilityRaw(char *pValue);
int selectNextAuthServer(void);
char *GetConfAuthServer(char *name);

/*** dummys ***/
void PutMessageToClient(char* buff){
    err_msg("ERR at %s#%d: %s",__FILE__,__LINE__,buff);
}
char* getProgramName(void){
  return "";
}


/**************************************************/
/* Prepare Conf file to use                       */
/* this should be called before syslog setup      */
/**************************************************/
int openConfFile(void)
{
  char buff[BUFFMAXLN];
  char *s;
  char *errMsg;

  /* parse file and make tree */
  if((xmlRoot = ezxml_parse_file(CONFIGFILE))==NULL){

    /* as the syslog is not prepared, error is send to web*/
    strlcpy(buff, "<H3>Error: Opengate configuration file ",BUFFMAXLN);
    strlcat(buff, CONFIGFILE,BUFFMAXLN);
    strlcat(buff, " is not found. Call the administrator.</H3><BR>",BUFFMAXLN);
    PutMessageToClient(buff);

    return -1;
  }

  /* try to convert to xml */
  s=ezxml_toxml(xmlRoot);  free(s);
  
  /* if failed, show error message */
  errMsg=(char *)ezxml_error(xmlRoot);
  if(*errMsg!='\0'){
    /* as the syslog is not prepared, error is send to web*/
    strlcpy(buff, "<H3>Error: Opengate configuration file ",BUFFMAXLN);
    strlcat(buff, CONFIGFILE,BUFFMAXLN);
    strlcat(buff, " is illegal. Call the administrator.</H3><HR>",BUFFMAXLN);
    strlcat(buff, "XML parser message: ", BUFFMAXLN);
    strlcat(buff, errMsg, BUFFMAXLN);
    strlcat(buff, "<HR>", BUFFMAXLN);
    PutMessageToClient(buff);
    return -1;
  }

  /* check the config file version */ 
  if(isNull(ezxml_attr(xmlRoot, "ConfigVersion"))||
     (strcmp(CONFIG_VERSION, ezxml_attr(xmlRoot, "ConfigVersion"))!=0)){
    strlcpy(buff, "<H3>Error: Opengate configuration file ",BUFFMAXLN);
    strlcat(buff, CONFIGFILE, BUFFMAXLN);
    strlcat(buff, " has mismatch version.<br> Please update it with ",BUFFMAXLN);
    strlcat(buff, CONFIGFILE, BUFFMAXLN);
    strlcat(buff, ".sample.",BUFFMAXLN);
    PutMessageToClient(buff);
    return -1;
  }

  /* check the syslog */
  if(atoi(GetConfValue("Syslog/Enable")) &&
     atoi(GetConfValue("Syslog/Facility"))==0){

    /* as the syslog is not prepared, error is send to web*/
    strlcpy(buff, "<H3>Error: correct SYSLOG setting(local0-local7) is not found in Opengate configuration file ",BUFFMAXLN);
    strlcat(buff, CONFIGFILE,BUFFMAXLN);
    strlcat(buff, ". Call the administrator.</H3><BR>",BUFFMAXLN);
    PutMessageToClient(buff);
    return -1;
  }

  return 0;
}

/**************************************************/
/*  initialize the Config                         */
/**************************************************/
void initConf(void)
{
  /* as debug flag is used many times, put it in gloval variable */
  debug=atoi(getConfValue("Debug"));
}

/**************************************************/
/* Finish Conf file usage                         */
/**************************************************/
void closeConfFile(void)
{
  if(xmlRoot!=NULL)ezxml_free(xmlRoot);
}

/**************************************************/
/* Setup pointer to the matched ExtraSet          */ 
/**************************************************/
void setupConfExtra(char * userId,char *extraId)
{
  char* progName;

  /* init as no ExtraSet */
  xmlExtraSet=NULL;

  /* search the matching extra set (first match is employed) */
  for(xml=ezxml_child(xmlRoot, "ExtraSet"); xml; xml=xml->next){
    
    /* if ExtraId is exist, check it */
    if(!isNull(ezxml_attr(xml, "ExtraId"))){

      /* if not match, go to next ExtraSet */
      /* ('default' indicated in conf matchs to Null-extraId) */
      if(isNull(extraId)){
	if(strcmp("default", ezxml_attr(xml, "ExtraId"))!=0)continue;
      }else{
	if(strcmp(extraId, ezxml_attr(xml, "ExtraId"))!=0)continue;
      }
    }

    /* if userID pattern is exist, check it */
    if(!isNull(ezxml_attr(xml, "UserIdPattern"))){

      /* if not matched, go to next ExtraSet */
      if(RegExMatch(userId, ezxml_attr(xml, "UserIdPattern"))==FALSE) continue;
    }

    /* found matched ExtraSet */
    break;
  }

  /* if found a matched ExtraSet, save the pointer */
  if(xml!=NULL) xmlExtraSet=xml;

  /* change syslog setting */
  errToSyslog(atoi(GetConfValue("Syslog/Enable")));
  progName=getProgramName();
  openlog(progName, LOG_PID, atoi(GetConfValue("Syslog/Facility")));

  /* reset config setting */
  InitConf();
}

/***********************************************/
/* regular expression matching                 */
/*  inStr : string to match                    */
/*  regEx : regular expression                 */
/***********************************************/
int regExMatch(const char *inStr, const char *regEx)
{
  regex_t reg;
  int errcode;
  int match;
  char errbuff[WORDMAXLN];

  /* compile regex */
  if((errcode=regcomp(&reg, regEx, REG_NOSUB|REG_EXTENDED|REG_ICASE))!=0){
    regerror(errcode, &reg, errbuff, WORDMAXLN);
    err_msg("ERR at %s#%d: regex message=%s",__FILE__,__LINE__,errbuff);
    match=FALSE;
  }
  
  /* if compile is success, check the input string */
  else{
    if(regexec(&reg, inStr, (size_t)0, NULL, 0)==0) match=TRUE;
    else match=FALSE;
  }

  regfree(&reg);

  return match;
}

/**************************************************/
/*  get a value for name from Conf file           */
/*  the name[aa/bb/cc] means the path             */
/*  if name is set in extraSet, the value is used */
/**************************************************/
char *getConfValue(char *name)
{
  char *pValue;
  char *pValueExtra;
  char *pStr;
  char buff[BUFFMAXLN];

  /* AuthServer setting is done in other routine */
  if(strstr(name,"AuthServer/")==name) return GetConfAuthServer(name);

  /* copy name to work area */
  strlcpy(buff,name,BUFFMAXLN);

  /* get first token */
  pStr=strtok(buff, SEPARATOR);

  /* set search start to root of tree */
  xml=xmlRoot;

  /* search the tree node for the name */
  while(pStr!=NULL){
    xml=ezxml_child(xml, pStr);
    pStr=strtok(NULL, SEPARATOR);
  }

  /* get the node value */
  pValue= ezxml_txt(xml);

  /* if not get, write error message */
  if(pValue==NULL){
    err_msg("ERR at %s#%d: cannot get %s from conf file",__FILE__,__LINE__,name);
  }

  /* get value in extra set matched to name */
  /* if name is matched in first level, reset all child setting */
  /* in this section, many parameters are not set */
  if(!isNull(pValueExtra=getConfValueExtra(name))){
    pValue=pValueExtra;
  }

  /* if syslog facility, the id is converted to raw value */
  if(strcmp(name,"Syslog/Facility")==0){
    pValue=convertToFacilityRaw(pValue);
  }

  /* return found value */
  return pValue;
}

/************************************************/
/* get the value in extra set matched to name   */
/************************************************/
char *getConfValueExtra(char *name)
{
  char *pStr;
  char buff[BUFFMAXLN];
  ezxml_t xml;

  if(xmlExtraSet==NULL) return "";

  /* extract first token in name */
  strlcpy(buff,name,BUFFMAXLN);
  pStr=strtok(buff, SEPARATOR);  

  /* get a first level matched node in extra set */
  /* the first level is not included in the following loop */
  /* as to prevent partial overlay of sub level value */
  xml=ezxml_child(xmlExtraSet, pStr);
  if(xml==NULL) return "";

  /* search the node matched to name */
  pStr=strtok(NULL, SEPARATOR);
  while(pStr!=NULL){
    xml=ezxml_child(xml, pStr);
    pStr=strtok(NULL, SEPARATOR);
  }

  /* return the found value */
  return ezxml_txt(xml);
}


/***************************************************/
/*  get a value for AuthServer param from Conf file*/
/*  the name[AuthServer/bb/cc] means the path      */
/***************************************************/
char *getConfAuthServer(char *name)
{
  char *pValue;
  char *pStr;
  char buff[BUFFMAXLN];
  ezxml_t xml;

  /* copy name to work area */
  strlcpy(buff,name,BUFFMAXLN);

  /* get first token */
  pStr=strtok(buff, SEPARATOR);

  /* it must be AuthServer. if not return */
  if(strcmp(pStr, "AuthServer")!=0)return NULL;

  /* if authserver pointer is not set, set it */
  if(xmlAuthServer==NULL){
    if(!selectNextAuthServer()) return NULL;
  }

  /* set search start to the saved pointer */
  xml=xmlAuthServer;

  /* search the tree node for the name */
  pStr=strtok(NULL, SEPARATOR);
  while(pStr!=NULL){
    xml=ezxml_child(xml, pStr);
    pStr=strtok(NULL, SEPARATOR);
  }

  /* get the node value */
  pValue= ezxml_txt(xml);

  /* if not get Protocol, write error message */
  if(isNull(pValue)
     && (strcmp(name,"AuthServer/Protocol")==0) ){
    err_msg("ERR at %s#%d: cannot get %s from conf file",__FILE__,__LINE__,name);
  }

  /* return found value */
  return pValue;
}

/**********************************/
/* select next authserver setting */
/* (move poiner to next server)   */
/**********************************/
int selectNextAuthServer(void){

  ezxml_t xmlTmp; /* temporary variable */

  /* first call (initialize) */
  /* xmlAuthPointer is the static variable to save authserver pointer */
  if(xmlAuthServer==NULL){

    /* if not set, search the first authserver pointer */
     xmlAuthServer=ezxml_child(xmlRoot, "AuthServer");
     
     /* if authserver is found in extra set, pointer is moved to it */ 
     if(xmlExtraSet!=NULL){
       xmlTmp=ezxml_child(xmlExtraSet, "AuthServer");
       if(xmlTmp!=NULL){
	 xmlAuthServer=xmlTmp;
       }
     }
  }

  /* successive calls */
  /* pointer is moved to next */
  else{
    xmlAuthServer=ezxml_next(xmlAuthServer);
  }

  /* if not found return False */
  if(xmlAuthServer==NULL){
    return FALSE;
  }else{
    return TRUE;
  }
}

/*********************************************/
/* reset pointer for auth server list        */
/*********************************************/
void resetAuthServerPointer(void){
  xmlAuthServer=NULL;
}

/***********************************************/
/* Convart the syslog facility id to raw value */
/*  in string form (only from local0 to local7)*/
/***********************************************/
char *convertToFacilityRaw(char *pValue)
{
  static char facility[WORDMAXLN];
  int rawValue;

  if     (strcmp(pValue, "local0")==0) rawValue=LOG_LOCAL0;
  else if(strcmp(pValue, "local1")==0) rawValue=LOG_LOCAL1;
  else if(strcmp(pValue, "local2")==0) rawValue=LOG_LOCAL2;
  else if(strcmp(pValue, "local3")==0) rawValue=LOG_LOCAL3;
  else if(strcmp(pValue, "local4")==0) rawValue=LOG_LOCAL4;
  else if(strcmp(pValue, "local5")==0) rawValue=LOG_LOCAL5;
  else if(strcmp(pValue, "local6")==0) rawValue=LOG_LOCAL6;
  else if(strcmp(pValue, "local7")==0) rawValue=LOG_LOCAL7;
  else rawValue=0;

  snprintf(facility, WORDMAXLN, "%d", rawValue);

  return facility;
}

/**************************************************/
/*  get the first value for the name              */
/**************************************************/
char *getFirstConfValue(char* name)
{
   char *pValue;
   pValue=GetConfValue(name);

  /* save the pointer now */
   xmlSave=xml;

  /* return found value */
  return pValue;
}

/**************************************************/
/*  get the next value                            */
/*  need to call after getFirstConfValue()        */
/**************************************************/
char *getNextConfValue(void)
{
  char *pValue;

  /* recover previous pointer */
  xml=xmlSave;

  /* get next node */
  if(xml==NULL) return "";
  xml = ezxml_next(xml);

  /* save for next call */
  xmlSave=xml;

  /* get the node value */
  pValue= ezxml_txt(xml);

  /* if not get, write error message */
  if(pValue==NULL) return "";

  /* return found value */
  return pValue;
}


/***********************************************/
/***********************************************/
int OpenConfFile(void){
  int ret;
  if(debug>1) err_msg("DEBUG:=>openConfFile( )");
  ret = openConfFile();
  if(debug>1) err_msg("DEBUG:(%d)<=openConfFile( )",ret);
  return ret;
}

void CloseConfFile(void){
  if(debug>1) err_msg("DEBUG:=>closeConfFile( )");
  closeConfFile();
  if(debug>1) err_msg("DEBUG:<=closeConfFile( )");
}

void SetupConfExtra(char *userId, char *extraId){
  if(debug>1) err_msg("DEBUG:=>setupConfExtra(%s,%s)",userId, extraId);
  setupConfExtra(userId, extraId);
  if(debug>1) err_msg("DEBUG:<=setupConfExtra( )");
}

char *GetConfValue(char *name){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getConfValue(%s)",name);
  ret=getConfValue(name);
  if(debug>1) err_msg("DEBUG:(%s)<=getConfValue( )",ret);
  return ret;
}

char *GetConfValueExtra(char *name){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getConfValueExtra(%s)",name);
  ret=getConfValueExtra(name);
  if(debug>1) err_msg("DEBUG:(%s)<=getConfValueExtra( )",ret);
  return ret;
}

char *GetConfAuthServer(char *name){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getConfAuthServer(%s)",name);
  ret=getConfAuthServer(name);
  if(debug>1) err_msg("DEBUG:(%s)<=getConfAuthServer( )",ret);
  return ret;
}

int SelectNextAuthServer(void){
  int ret;
  if(debug>1) err_msg("DEBUG:=>selectNextAuthServer( )");
  ret=selectNextAuthServer();
  if(debug>1) err_msg("DEBUG:(%d)<=selectNextAuthServer( )",ret);
  return ret;
}

void InitConf(void){
  if(debug>1) err_msg("DEBUG:=>initConf( )");
  initConf();
  if(debug>1) err_msg("DEBUG:<=initConf( )");
}

int RegExMatch(const char *inStr, const char *regEx){
  int ret;
  if(debug>1) err_msg("DEBUG:=>regExMatch(%s,%s)", inStr, regEx);
  ret=regExMatch(inStr, regEx);
  if(debug>1) err_msg("DEBUG:(%d)<=regExMatch( )",ret);
  return ret;
}
  
void ResetAuthServerPointer(void){
  if(debug>1) err_msg("DEBUG:=>resetAuthServerPointer( )");
  resetAuthServerPointer();
  if(debug>1) err_msg("DEBUG:<=resetAuthServerPointer( )");
}

char *GetFirstConfValue(char* name){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getFirstConfValue( )");
  ret=getFirstConfValue(name);
  if(debug>1) err_msg("DEBUG:(%s)<=getFirstConfValue( )",ret);
  return ret;
}

char *GetNextConfValue(void){
  char *ret;
  if(debug>1) err_msg("DEBUG:=>getNextConfValue( )");
  ret=getNextConfValue();
  if(debug>1) err_msg("DEBUG:(%s)<=getNextConfValue( )",ret);
  return ret;
}

