#define PAT_MAX 20
#include <stdio.h>
#include <errno.h>
#include <pcre.h>
#include <string.h>
#include "vars.h"


void get_opt(char* config_file)
{
 FILE *conf;
 pcre* pat[PAT_MAX];
 pcre_extra *spat[PAT_MAX];
 char pattern[PAT_MAX][100],etmp[ML_ETMP],tmp[ML_CFG],*rez;

 const char *errstr;
 int i,z,errchar,h_flag=0,vector[10],pairs,vecsize=10;
 #ifdef DEBUG_CFG
 int j;
 #endif

  // Set pattern
  sprintf(pattern[0],"^\\s*[\\#\\;]");
  sprintf(pattern[1],"^\\s*error_log\\s+(\\S+)");
  sprintf(pattern[2],"^\\s*change_log\\s+(\\S+)");
  sprintf(pattern[3],"^\\s*\\<(\\S+)\\>");
  sprintf(pattern[4],"^\\s*ban_dir\\s+(\\S+)");
  sprintf(pattern[5],"^\\s*url\\s+(\\S+)");
  sprintf(pattern[6],"^\\s*allow_ip\\s+(\\S+)");
  sprintf(pattern[7],"^\\s*work_ip\\s+(\\S+)");
  sprintf(pattern[8],"^\\s*allow_id\\s+(\\S+)");
  sprintf(pattern[9],"^\\s*reverse");
  sprintf(pattern[10],"^\\s*log\\s+(\\S+)");
  sprintf(pattern[11],"^\\s*");
  sprintf(pattern[12],"^\\s*allow_urls\\s+(\\S+)");
  sprintf(pattern[13],"^\\s*make-cache\\s+(\\S+)");
  sprintf(pattern[14],"^\\s*work_id\\s+(\\S+)");
  sprintf(pattern[15],"^\\s*raw_change\\s+(\\S+)\\s+(\\S+)");
  sprintf(pattern[16],"^\\s*raw_log\\s+(\\S+)");
  sprintf(pattern[17],"^\\s*action\\s+(\\S+)");
  sprintf(pattern[18],"^\\s*dbl_");
  sprintf(pattern[19],"^\\s*write_hostname_to_log on");

  // Init vars
  err_log=NULL;
  chg_log=NULL;
  allow_urls_file=NULL;

  work_ip=NULL;
  work_ip_count=0;
  work_ip_flag=0;

  allow_ip=NULL;
  allow_ip_count=0;

  work_id=NULL;
  work_id_count=0;
  work_id_flag=0;

  allow_id=NULL;
  allow_id_count=0;

  redir=NULL;
  sections_count=0;
  raw_change_log_flag=1;

   //Load patterns
   for(i=0;i<PAT_MAX;i++)
   {
     //Compile pattern
     if((pat[i]=pcre_compile(pattern[i],PCRE_CASELESS,&errstr,&errchar,NULL))==NULL)
        {
         sprintf(tmp,"ERROR: Can't compile pattern: i=%i %s\n%s\n",i,errstr,pattern[i]);
         printf("%s\n",tmp);
         exit(-1);
        }

     //Optimize patterns
     spat[i]=pcre_study(pat[i],0,&errstr);
   }


 //Open redirector.conf
 if ((conf=fopen(config_file,"r"))==NULL)
       {
        printf("ERROR: Can't open config file %s: %s\n",config_file,strerror(errno));
	printf("Use: redirector /path/to/redirector.conf\n");
	printf("Use: make-cache /path/to/redirector.conf\n");
	printf("by default: %s\n\n",DEFAULT_CONFIG);
        exit(-1);
       }

//Parse config strings by error_log
while (!feof(conf))
{
    fgets(tmp,ML_CFG,conf);

  // Comment "#"
    pairs=pcre_exec(pat[0],spat[0],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
      continue;

  // Ignoring dbl_
    pairs=pcre_exec(pat[18],spat[18],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
      continue;

  // error log
    pairs=pcre_exec(pat[1],spat[1],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME)
      {
       printf("ERROR: Len of error log name higher %i symbols. Exit.\n",ML_FILE_NAME);
       exit(-1);
      }
      err_log=strdup(rez);
      continue;
     }
}
 fclose (conf);

 //Open redirector.conf
 if ((conf=fopen(config_file,"r"))==NULL)
       {
        printf("ERROR: Can't open config file %s: %s\n",config_file,strerror(errno));
	printf("Use: redirector /path/to/redirector.conf\n");
	printf("Use: make-cache /path/to/redirector.conf\n");
	printf("by default: %s\n\n",DEFAULT_CONFIG);
        exit(-1);
       }

while (!feof(conf))
{
    fgets(tmp,ML_CFG,conf);

  // Comment "#"
    pairs=pcre_exec(pat[0],spat[0],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
      continue;

  // Ignoring dbl_
    pairs=pcre_exec(pat[18],spat[18],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
      continue;

   if(!h_flag)
   {
  // error log
    pairs=pcre_exec(pat[1],spat[1],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME)
      {
       printf("ERROR: Len of error log name higher %i symbols. Exit.\n",ML_FILE_NAME);
       exit(-1);
      }
      err_log=strdup(rez);
      continue;
     }

   // change log
    pairs=pcre_exec(pat[2],spat[2],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME)
      {
       sprintf(etmp,"ERROR: Len of change log name higher %i symbols. Exit.",ML_FILE_NAME);
       err_mes(etmp);
       exit(-1);
      }
      chg_log=strdup(rez);
      continue;
     }

   // make-cache path
    pairs=pcre_exec(pat[13],spat[13],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME)
      {
       sprintf(etmp,"ERROR: Len of makecache name higher %i symbols. Exit.",ML_FILE_NAME);
       err_mes(etmp);
       exit(-1);
      }
      makecache=strdup(rez);
      continue;
     }

   // Allow_urls_file
    pairs=pcre_exec(pat[12],spat[12],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME)
      {
       sprintf(etmp,"ERROR: Len of allow_urls file name higher %i symbols. Exit.",ML_FILE_NAME);
       err_mes(etmp);
       exit(-1);
      }

      allow_urls_file=strdup(rez);
      continue;
     }

   // Change line
    pairs=pcre_exec(pat[15],spat[15],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      //FROM
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_URL)
      {
       sprintf(etmp,"ERROR: Len of change option higher %i symbols. Exit.",ML_URL);
       err_mes(etmp);
       exit(-1);
      }

	//Add memory line
	change_from=(char **)realloc(change_from,(1+change_count)*sizeof(char**));
	if (change_from==NULL)
	   {
	    err_mes("Error: Can't allocate memory for change_from\n");
	    exit(-1);
           }
	change_from[change_count]=strdup(rez);
	 if (change_from[change_count]==NULL)
	   {
	    err_mes("Error: Can't strdup for change_from\n");
	    exit(-1);
           }

      //TO
      rez=tmp+vector[4];
      tmp[vector[5]]=0;
      //Check to len of file name
      if (strlen(rez)>ML_URL)
      {
       sprintf(etmp,"ERROR: Len of change option higher %i symbols. Exit.",ML_URL);
       err_mes(etmp);
       exit(-1);
      }
    	//Add memory line
	change_to=(char **)realloc(change_to,(1+change_count)*sizeof(char**));
	if (change_from==NULL)
	   {
	    err_mes("Error: Can't allocate memory for change_to\n");
	    exit(-1);
           }
	change_to[change_count]=strdup(rez);
	 if (change_to[change_count]==NULL)
	   {
	    err_mes("Error: Can't strdup for change_to\n");
	    exit(-1);
           }

      change_count++;
      continue;
     }

    // RAW log flag
    pairs=pcre_exec(pat[16],spat[16],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      if (strcmp(rez,"off")) raw_change_log_flag=1;
      else raw_change_log_flag=0;
      continue;
     }
    
    // write_hostname_to_log flag
    pairs=pcre_exec(pat[19],spat[19],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      if (strcmp(rez,"on")) write_hostname_to_log=1;
      else raw_change_log_flag=0;
      continue;
     }
     
    }// ------------------------------
    
    
   // First <HEADER>
    pairs=pcre_exec(pat[3],spat[3],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(!h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;

	//Add memory line
	redir=(struct section**)realloc(redir,(1+sections_count)*sizeof(struct section*));
	if (redir==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir struct\n");
	    exit(-1);
           }
	redir[sections_count]=(struct section*) malloc((1+sections_count) * sizeof(struct section));
	   if (redir[sections_count]==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir struct\n");
	    exit(-1);
           }
	memset(redir[sections_count],0,sizeof(struct section));

      //Set name of section
	redir[sections_count]->name=strdup(rez);
	 if (redir[sections_count]->name==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir->name\n");
	    exit(-1);
           }
      h_flag=1;
      continue;
     }

   // Next <HEADER>
    if(h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      sections_count++;

	//Add memory line
	redir=(struct section**)realloc(redir,(1+sections_count)*sizeof(struct section*));
	if (redir==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir struct\n");
	    exit(-1);
           }
	redir[sections_count]=(struct section*) malloc((1+sections_count) * sizeof(struct section));
	   if (redir[sections_count]==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir struct\n");
	    exit(-1);
           }
	memset(redir[sections_count],0,sizeof(struct section));
        //Set name of section
	redir[sections_count]->name=strdup(rez);
	 if (redir[sections_count]->name==NULL)
	   {
	    err_mes("Error: Can't allocate memory for redir->name\n");
	    exit(-1);
           }

      continue;
     }

   // ban_dir
    pairs=pcre_exec(pat[4],spat[4],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;

      //Check to len of file name
      if (strlen(rez)>ML_FILE_NAME-20)
      {
       sprintf(etmp,"ERROR: Len of ban_dir name higher %i symbols. Exit.",ML_FILE_NAME);
       err_mes(etmp);
       exit(-1);
      }

      redir[sections_count]->ban_dir=strdup(rez);
      if (redir[sections_count]->ban_dir==NULL)
          {
	    err_mes("Error: Can't allocate memory for redir->ban_dir\n");
	    exit(-1);
           }
      continue;
     }

   // revers option
    pairs=pcre_exec(pat[9],spat[9],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(h_flag && pairs>=0)
     {
      redir[sections_count]->revers=1;
      continue;
     }

   // url
    pairs=pcre_exec(pat[5],spat[5],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;

      //Check to len of url
      if (strlen(rez)>ML_FILE_NAME)
      {
       sprintf(etmp,"ERROR: Len of url higher %i symbols. Exit.",ML_FILE_NAME);
       err_mes(etmp);
       exit(-1);
      }

      redir[sections_count]->url=strdup(rez);
      if (redir[sections_count]->url==NULL)
          {
	    err_mes("Error: Can't allocate memory for redir->url\n");
	    exit(-1);
           }

      continue;
     }

  // log_flag
    pairs=pcre_exec(pat[10],spat[10],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      if (strcmp(rez,"off")) redir[sections_count]->log_flag=0;
      else redir[sections_count]->log_flag=1;
      continue;
     }


   // work_ip
    pairs=pcre_exec(pat[7],spat[7],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2]; tmp[vector[3]]=0;

      if (!h_flag) //for global ip_allow
        {
	 work_ip_flag=1;
         add_ip(rez,&work_ip,&work_ip_count);
	}
      else // for section ip_allow
        {
	 redir[sections_count]->work_ip_flag=1;
         add_ip(rez,&redir[sections_count]->work_ip,&redir[sections_count]->work_ip_count);
	}

       continue;
     }

   // allow_ip
    pairs=pcre_exec(pat[6],spat[6],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2]; tmp[vector[3]]=0;

      if (!h_flag) //for global ip_allow
        add_ip(rez,&allow_ip,&allow_ip_count);
      else // for section ip_allow
        add_ip(rez,&redir[sections_count]->allow_ip,&redir[sections_count]->allow_ip_count);

       continue;
     }

   // allow_id
    pairs=pcre_exec(pat[8],spat[8],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2]; tmp[vector[3]]=0;

      if (!h_flag) //for global allow_id
        add_id(rez,&allow_id,&allow_id_count);
      else // for section allow_id
        add_id(rez,&redir[sections_count]->allow_id,&redir[sections_count]->allow_id_count);

       continue;
     }

   // work_id
    pairs=pcre_exec(pat[14],spat[14],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
     {
      rez=tmp+vector[2]; tmp[vector[3]]=0;

      if (!h_flag) //for global allow_id
       {
        work_id_flag=1;
        add_id(rez,&work_id,&work_id_count);
       }
      else // for section allow_id
       {
        redir[sections_count]->work_id_flag=1;
        add_id(rez,&redir[sections_count]->work_id,&redir[sections_count]->work_id_count);
       }

       continue;
     }

   // Action
    pairs=pcre_exec(pat[17],spat[17],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(h_flag && pairs>=0)
     {
      rez=tmp+vector[2];
      tmp[vector[3]]=0;
      if (strcmp(rez,"pass")) redir[sections_count]->action=0;
      else redir[sections_count]->action=1;
      continue;
     }


    // empty line
    pairs=pcre_exec(pat[11],spat[11],tmp,strlen(tmp),0,PCRE_NOTEMPTY,vector,vecsize);
    if(pairs>=0)
    continue;

    //Understand options
    sprintf(etmp,"WARNING!\n Understand option in config file: %s ... Ignoring",tmp);
    err_mes(etmp);

} //while
fclose(conf);
sections_count+=h_flag;

/********** Set log flag *************************/

if (!chg_log)
for (z=0;z<sections_count; z++)
redir[z]->log_flag=1;

/********** Sort work_id & allow_id ************************/
if (allow_id_count) sort_id(&allow_id,&allow_id_count);
if (work_id_count) sort_id(&work_id,&work_id_count);

for (z=0;z<sections_count; z++)
{
 if (redir[z]->allow_id_count) sort_id(&redir[z]->allow_id,&redir[z]->allow_id_count);
 if (redir[z]->work_id_count) sort_id(&redir[z]->work_id,&redir[z]->work_id_count);
}

/********** Check for error in config file *******/
if (!makecache)
   {
    sprintf(etmp,"Error in config file: Can't find \"make-cache path_to_make-cache\" option");
    err_mes(etmp);
    exit(-1);
   }

for (z=0;z<sections_count; z++)
{
 if (redir[z]->name==NULL)
       {
	sprintf(etmp,"Error interpretation <HEADER> in config file");
	err_mes(etmp);
	exit(-1);
       }
 if (redir[z]->ban_dir==NULL)
       {
	sprintf(etmp,"Error in config file: not found ban_dir option for %s",redir[z]->name);
	err_mes(etmp);
	exit(-1);
       }

 if (redir[z]->url==NULL)
       {
        if (redir[z]->action==1)
        {
         redir[z]->url=strdup("none");
        }
        else
        {
 	 sprintf(etmp,"Error in config file: not found url option for %s",redir[z]->name);
	 err_mes(etmp);
	 exit(-1);
	}
       }
}
#ifdef DEBUG_CFG
fprintf(stderr,"\n========== GLOBAL ===========\n");
if (err_log)  fprintf(stderr," error log:%s\n",err_log);
else fprintf(stderr," error log: none\n");

if (chg_log)  fprintf(stderr,"change log:%s\n",chg_log);
else fprintf(stderr,"change log: none\n");

  fprintf(stderr,"make-cache:%s\n",makecache);

if (allow_urls_file)  fprintf(stderr,"allow urls file: %s\n",allow_urls_file);
else fprintf(stderr,"allow urls file: none\n");

fprintf(stderr,"work_ip_count: %i (flag:%i)\n",work_ip_count,work_ip_flag);
 for (j=0;j< work_ip_count*8;j+=8)
    fprintf (stderr,"     work_ip: %i %i.%i.%i.%i/%i.%i.%i.%i\n",1+j/8,work_ip[j],work_ip[j+1],work_ip[j+2],work_ip[j+3],work_ip[j+4],work_ip[j+5],work_ip[j+6],work_ip[j+7]);

fprintf(stderr,"Allow_ip_count: %i\n",allow_ip_count);
 for (j=0;j< allow_ip_count*8;j+=8)
    fprintf (stderr,"    allow_ip: %i %i.%i.%i.%i/%i.%i.%i.%i\n",1+j/8,allow_ip[j],allow_ip[j+1],allow_ip[j+2],allow_ip[j+3],allow_ip[j+4],allow_ip[j+5],allow_ip[j+6],allow_ip[j+7]);


fprintf(stderr,"Work_id_count: %i (flag:%i)\n",work_id_count,work_id_flag);
     for(j=0;j<work_id_count;j++)
       fprintf(stderr,"  work-id: %s\n",work_id[j]);

fprintf(stderr,"Allow_id_count: %i\n",allow_id_count);
     for(j=0;j<allow_id_count;j++)
       fprintf(stderr,"  allow-id: %s\n",allow_id[j]);

fprintf(stderr,"change_count: %i\n",change_count);
  for(j=0;j<change_count;j++)
       fprintf(stderr,"  change: %s -> %s\n",change_from[j],change_to[j]);

 if (raw_change_log_flag) fprintf(stderr,"Raw change to log: ON\n");
 else  fprintf(stderr,"Raw change to log: OFF\n");




fprintf(stderr,"Redir counter: %i\n",sections_count);

    for (z=0;z<sections_count; z++)
    {
     fprintf (stderr,"===========================\n");
     fprintf (stderr," Name: %s\n",redir[z]->name);
     fprintf (stderr,"        Dir: %s\n",redir[z]->ban_dir);
     fprintf (stderr,"        URL: %s\n",redir[z]->url);
     fprintf (stderr,"     Action: %i (0-redirect,1-pass)\n",redir[z]->action);
     fprintf (stderr,"   Log_flag: %i (0-on,1-off)\n",redir[z]->log_flag);
     fprintf (stderr,"revers_flag: %i (0-on,1-off)\n",redir[z]->revers);


fprintf(stderr,"work_ip_count: %i (flag:%i)\n",redir[z]->work_ip_count,redir[z]->work_ip_flag);
 for (j=0;j< redir[z]->work_ip_count*8;j+=8)
    fprintf (stderr,"     work_ip: %i %i.%i.%i.%i/%i.%i.%i.%i\n",1+j/8,redir[z]->work_ip[j],redir[z]->work_ip[j+1],redir[z]->work_ip[j+2],redir[z]->work_ip[j+3],redir[z]->work_ip[j+4],redir[z]->work_ip[j+5],redir[z]->work_ip[j+6],redir[z]->work_ip[j+7]);

fprintf(stderr,"Allow_ip_count: %i\n",redir[z]->allow_ip_count);
   for (j=0;j< redir[z]->allow_ip_count*8;j+=8)
    fprintf (stderr," allow_ip: %i %i.%i.%i.%i/%i.%i.%i.%i\n",1+j/8,redir[z]->allow_ip[j],redir[z]->allow_ip[j+1],redir[z]->allow_ip[j+2],redir[z]->allow_ip[j+3],redir[z]->allow_ip[j+4],redir[z]->allow_ip[j+5],redir[z]->allow_ip[j+6],redir[z]->allow_ip[j+7]);

fprintf(stderr,"Work_id_count: %i (flag:%i)\n",redir[z]->work_id_count,redir[z]->work_id_flag);
     for(j=0;j<redir[z]->work_id_count;j++)
       fprintf(stderr,"  work-id: %s\n",redir[z]->work_id[j]);

fprintf(stderr,"Allow_id_count: %i\n",redir[z]->allow_id_count);
     for(j=0;j<redir[z]->allow_id_count;j++)
     {
       fprintf(stderr,"  allow-id: %s\n",redir[z]->allow_id[j]);
     }
    }


#endif
}
