/*****************************************************************
 * PTlink IRCd is (C) CopyRight PTlink Coders Team 1999-2001     *
 * http://www.ptlink.net/Coders - coders@PTlink.net              *
 * This program is distributed under GNU Public License          *
 * Please read the file COPYING for copyright information.       *
 *****************************************************************
 
  File: dconf.c
  Desc: general .dconf parsing 
  Author: Lamego@PTlink.net
*/

#include "common.h"
#include "s_log.h"
#include "irc_string.h"
#include "m_commands.h"
#include "ircd.h"
#include "send.h"
#include "client.h"
#include "numeric.h"
#include "dconf.h"
#include "conf_vars.h"

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>

extern void		spam_words_init(const char *);
extern void             virus_words_init(const char *);

char* HostPrefix = NULL;
char *TimeZone = NULL;
static 
    ConfItem conf_items[] = {
	/* Server configuration */
	{ "Server configuration", CT_DESC, NULL, 0},
	{ "HostPrefix",		    CT_STRING, &HostPrefix, CF_REQ },
	{ "IsHub", 				   CT_SET, &IsHub, CF_REQ },
	{ "TimeZone",		  	CT_STRING, &TimeZone, 0 },
	
	{ NULL,	0,	NULL, 0 }
};

/*
 * conf_find_item
 *
 * inputs       - item name
 * output       - item index
 * side effects - looks for a specific item name on the conf items list
 */
int conf_find_item(char *name) {
    int i=0;
	
    while (conf_items[i].name && strcasecmp(conf_items[i].name, name))
	  ++i;
	  
    return conf_items[i].name ? i : -1;
}

/*
 * conf_item_str
 *
 * inputs       - item index
 * output       - item value (as string)
 * side effects - returns a string with the item value
 */
char* conf_item_str(int i) {
  static char tmpbuf[512];
  int tmpval;
  
  tmpbuf[0]='0';
  
  if(!conf_items[i].name)
	return tmpbuf;
	
  if(!(conf_items[i].flags & CF_DEFINED))	
	{
	  strcpy(tmpbuf,"Undefined");
	}
  else
	{
    switch(conf_items[i].type) 
	  {	
		case CT_DESC:
		break;		
		  
		case CT_STRING:	
		  ircsprintf(tmpbuf,"\"%s\"",*(char**) conf_items[i].vptr);
	  	break;
		
		case CT_SET:
		if (*(int*)conf_items[i].vptr == -1)
		  ircsprintf(tmpbuf,"Yes");
		else
		  ircsprintf(tmpbuf,"No");
		break;
			
		case CT_TIME:
		  tmpval = *(int*) conf_items[i].vptr;
		  if(tmpval>59)
			ircsprintf(tmpbuf,"%dm",tmpval / 60);
		  else
	  		ircsprintf(tmpbuf,"%ds",tmpval);
		break;
		
		case CT_INT:
		    tmpval = *(int*) conf_items[i].vptr;
	  		ircsprintf(tmpbuf,"%i",tmpval);
		break;
		
		default:
		  strcpy(tmpbuf,"Unknown format");
  	  }	
	  
    }
	
	return tmpbuf;	
}

/*
 * conf_change_item
 *
 * inputs       - item name, new value, rehashing state
 * output       - change success
 * side effects - log any possible problem with the new setting
 */
int  conf_change_item(char *name, char* value, int rehashing) 
  {
    int tmpval;
	
    int i = conf_find_item(name);

	
    if(i<0)
	  {
		if(!rehashing)
		  ircd_log(L_WARN,"Ignoring unknown directive %s", name);
		return (UNKNOWN_ITEM);
	  }
	  	  
	while(IsSpace(*value)) 
		  ++value;

	/* Let's remove the surrounding "" */
	if ((*value) == '\"') 
		*(value++)=' ';	
			  
	if (*value && (value[strlen(value)-1] == '\"') )
		value[strlen(value)-1]='\0';
		  
	if ((rehashing<2) && (conf_items[i].flags & CF_REQ) && (value[0]=='\0')) /* cannot undefine */
		  return (LOCKED_ITEM);
	  	  
    if (conf_items[i].flags & CF_DEFINED) 
	  { /* this value is already defined  */
		if (rehashing) 
		  {
	  		if ((rehashing==1) && (conf_items[i].flags & CF_LOCKED)) /* item is locked */
			  return (LOCKED_ITEM);
	  		else 
			  {
				if (conf_items[i].type==CT_STRING) 
	  			  free( *(char**) conf_items[i].vptr);
	  		  }	    
		  }	
		else 
		  {
	  		ircd_log(L_WARN,"Ignoring duplicated directive %s", name);		
	  		return 0;
		  }
  	  }
	
    switch(conf_items[i].type) 
	  {	
		case CT_DESC:
		break;		
		  
		case CT_STRING:	
			
		  /* Let's remove the surrounding "" */
		  if ((*value) == '\"') 
			*(value++)=' ';	
				  
		  if (*value && (value[strlen(value)-1] == '\"') )
			value[strlen(value)-1]='\0';
			
		  if(value[0]=='\0')
			{
	  	  	  *(char **)conf_items[i].vptr = NULL;
			  conf_items[i].flags &= ~CF_DEFINED;
			  return 1;
			}
		  else /* save the new data */
	  		*(char **)conf_items[i].vptr = (value[0]) ? strdup(value) : NULL;
	  	break;
		
		case CT_SET:
		  if ( strcasecmp(value,"yes") == 0)
			*(int*) conf_items[i].vptr = -1;
		  else if ( strcasecmp(value,"no") == 0)
			*(int*) conf_items[i].vptr = 0;
		  else 
		  {
		  	if (!rehashing)
			  ircd_log(L_WARN,"Invalid value (%s) on .conf item %s, please use Yes/No", value, name);
		    return (INV_SET_ITEM);
		  }		  
		break;
			
		case CT_TIME:
	  	  tmpval = time_str(value);
	  	  if (tmpval<=0) 
			{ 
			  if (!rehashing)
		  		ircd_log(L_WARN,"Invalid time format on .conf item %s", name);
    		  return (INV_TIME_ITEM);
	  		} 			  
	  	  else *(int*) conf_items[i].vptr = tmpval;
		  conf_items[i].flags |= CF_DEFINED;
		break;
		  
		default:
		  *(int*) conf_items[i].vptr = atoi(value);
  	  }
	    	
	conf_items[i].flags |= CF_DEFINED;	
    return 1;
  }


/*
 * conf_read
 *
 * inputs       - .conf filename and rehash mode
 * output       - number of errors during .conf reading
 * side effects - log any possible problems
 */
int conf_read(char *fn, int rehashing) 
  {
	char line[512];	/* buffer to read .conf line */
    FILE *confile;
    char* directive;
    char* value;
	char* auxptr;
    int	i;
	int errors = 0; /* errors found reading conf */
	
	if(rehashing)
	  ircd_log(L_NOTICE,"Reading configuration file %s (during rehash)", fn);
	else
	  ircd_log(L_NOTICE,"Reading configuration file %s", fn);
	
    confile = fopen(fn,"rt");
    if(!confile) 
	  {
		return -1;
  	  }	
	  
    while (!feof(confile)) 
	  {
	  
		fgets(line, sizeof(line), confile);		
		auxptr = strchr(line,'#');
		
		auxptr = line;
		
		while(IsSpace(*auxptr))
			++auxptr;
					
		directive = strtok(auxptr,"\t\n ");
		
		if( !directive || (directive[0] == '#') )
		  continue;
		  
		value = strtok(NULL,"\r\n");

		if (!value) 
		  {
	    	ircd_log(L_WARN,"Missing value for directive %s reading %s", directive, fn);
			continue;
		  }
		
		while(IsSpace(*value)) 
		  ++value;
		
		if ( !strcmp(directive,".include") )
		  {
			if (conf_read(value, rehashing) == -1)
			  {
				ircd_log(L_ERROR,"Error opening include file %s",value);
			  }
			  continue;
		  }
		  		  
		i = conf_change_item(directive, value, rehashing);
		

		if(i<0)
		  ++errors;

	  }
	  
	fclose(confile);
	
	if(errors)
	  {
		ircd_log(L_ERROR,"%s had %i errors", fn, errors);
	  }	  	
	
    return errors;	  
  }
/*
 * conf_check
 *
 * inputs       - verbose if true errors will be printed to stderr
 * output       - 1 if valid conf, 0 otherwise
 * side effects - check if any required item is missing
 */

int conf_check(int verbose) 
  {
	int i = 0;
	int errors = 0;
	int flags;
	while (conf_items[i].name)
	  {
		flags = conf_items[i].flags;
		if ((flags & CF_REQ) && !(flags & CF_DEFINED))
		  {
            if(verbose)
              fprintf(stderr,"Missing required directive: %s\n", conf_items[i].name);
            else
			  ircd_log(L_ERROR,"Missing required directive: %s", conf_items[i].name);
			++errors;
		  }
		  ++i;
	  }	  
	  
	if(errors) 
	  {
		ircd_log(L_ERROR,"Terminating because of %i required item(s)", errors);
		return 0;
	  }	
	  
	return 1;
  }

/*
 * m_functions execute protocol messages on this server:
 *
 *      cptr    is always NON-NULL, pointing to a *LOCAL* client
 *              structure (with an open socket connected!). This
 *              identifies the physical socket where the message
 *              originated (or which caused the m_function to be
 *              executed--some m_functions may call others...).
 *
 *      sptr    is the source of the message, defined by the
 *              prefix part of the message if present. If not
 *              or prefix not found, then sptr==cptr.
 *
 *              (!IsServer(cptr)) => (cptr == sptr), because
 *              prefixes are taken *only* from servers...
 *
 *              (IsServer(cptr))
 *                      (sptr == cptr) => the message didn't
 *                      have the prefix.
 *
 *                      (sptr != cptr && IsServer(sptr) means
 *                      the prefix specified servername. (?)
 *
 *                      (sptr != cptr && !IsServer(sptr) means
 *                      that message originated from a remote
 *                      user (not local).
 *
 *              combining
 *
 *              (!IsServer(sptr)) means that, sptr can safely
 *              taken as defining the target structure of the
 *              message in this server.
 *
 *      *Always* true (if 'parse' and others are working correct):
 *
 *      1)      sptr->from == cptr  (note: cptr->from == cptr)
 *
 *      2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
 *              *cannot* be a local connection, unless it's
 *              actually cptr!). [MyConnect(x) should probably
 *              be defined as (x == x->from) --msa ]
 *
 *      parc    number of variable parameter strings (if zero,
 *              parv is allowed to be NULL)
 *
 *      parv    a NULL terminated list of parameter pointers,
 *
 *                      parv[0], sender (prefix string), if not present
 *                              this points to an empty string.
 *                      parv[1]...parv[parc-1]
 *                              pointers to additional parameters
 *                      parv[parc] == NULL, *always*
 *
 *              note:   it is guaranteed that parv[0]..parv[parc-1] are all
 *                      non-NULL pointers.
 */

/*
** m_dconf - general conf handling routine
**      parv[0] = sender prefix
**      parv[1] = command (list/rehash/set)
**      parv[2] = setting item  (or mask for /list)
**      parv[3] = new value (only required for set)
*/
int	m_dconf(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  FILE *confile;
  int errors = 0;
  int res;
  char *cmask = NULL;
  char reply[100];
  int i;
  char *item, *newval;
  
  if ( !IsSAdmin(sptr) && !IsService(sptr) ) 
	{	
      sendto_one(sptr, form_str(ERR_NOPRIVILEGES), MENAME(sptr), parv[0]);
      return 0;
	}

  if (parc < 2 || *parv[1] == '\0')
    {
	  if(MyClient(sptr))
    	  sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS),
                 MENAME(sptr), parv[0], "DCONF");
      return -1;
    }

	
  if ( !strcasecmp(parv[1],"rehash") )
	{
	
  	  sendto_one(sptr, ":%s NOTICE %s :Rehashing main.conf",
           me.name,parv[0]);		  	
		   
	  if( (errors = conf_read("main.dconf", 2)) )
		{
      	  sendto_one(sptr, ":%s NOTICE %s :*** (%i) errors found",
           me.name,parv[0], errors);		  
		}
	  else
	    sendto_one(sptr, ":%s NOTICE %s :*** Rehash terminated without errors",
           me.name,parv[0]);
	}
  else if ( !strcasecmp(parv[1],"list") )
	{
	  if (parc>1)
		cmask = parv[2];
		
	
	  sendto_one(sptr, ":%s NOTICE %s :---------- Start of dconf list ----------",
		  me.name,parv[0]);
		  
	  i = 0;

  	  while (conf_items[i].name)
		{
		  if (!cmask || match(cmask, conf_items[i].name))
			{
			if(conf_items[i].type == CT_DESC) 
			  {
		  		if(i)
				  sendto_one(sptr, ":%s NOTICE %s : ",
					me.name,parv[0]);			
		  		sendto_one(sptr, ":%s NOTICE %s :[ %s ]",
				  me.name,parv[0], conf_items[i].name);				  
			  }
			else
			  sendto_one(sptr, ":%s NOTICE %s :%s = %s",
				  me.name,parv[0],
				  conf_items[i].name, conf_item_str(i));
			}
		  ++i;
		}	  
	  
  	  sendto_one(sptr, ":%s NOTICE %s :---------- End of dconf list ----------",		
		  me.name,parv[0]);	  
	}
  else if ( !strcasecmp(parv[1],"set") )
	{
	  if (parc<4) 
		{
		  sendto_one(sptr, ":%s NOTICE %s :Usage DCONF SET <option> <value>",
			me.name, parv[0]);
		  return -2;
		}
		
	  item = parv[2];
	  newval = parv[3];
	  
	  if(strlen(item) > 40)
		item[40]='\0';
  	  if(strlen(newval) > 40)
		newval[40]='\0';
		
	  res = conf_change_item(item, newval, 1);
	  reply[0]='\0';
	  
	  switch(res) 
		{
		  case UNKNOWN_ITEM: 
			ircsprintf(reply,"Unknown item \2%s\2",item);
			break;
		  case LOCKED_ITEM:
			ircsprintf(reply,"Item \2%s\2 cannot be changed", item);
			break;
		  case INV_SET_ITEM: 
			ircsprintf(reply,"Invalid value \2%s\2, please use Yes/No", newval);
		  	break;
		  case INV_TIME_ITEM: 
			ircsprintf(reply,"Invalid time value \2%s\2, please use NNd|NNm|NNs", newval);
		  	break;
		  default:
		    ircsprintf(reply,"%s = \2%s\2", item, newval);		  			
			break;
		}
			  
//        ircd_log(L_NOTICE,"DCONF SET from %s : %s", get_client_name(sptr, REAL_IP), reply);
		  
	  if(IsService(sptr))
		sendto_serv_butone(cptr, ":%s DCONF SET %s :%s", parv[0], item, newval);
	  else
		{
		  sendto_one(sptr, ":%s NOTICE %s :%s", me.name, parv[0], reply);				
		}
	}
  else if ( !strcasecmp(parv[1],"save") )
	{
//        ircd_log(L_NOTICE,"DCONF SAVE from %s", get_client_name(sptr, REAL_IP));

	  if(!IsService(sptr))
		sendto_one(sptr, ":%s NOTICE %s :--- Saving dconf settings to save.dconf ",
			me.name,parv[0]);
		  
	  confile = fopen("save.dconf","w");
	  if(!confile) 
		{
			sendto_one(sptr, ":%s NOTICE %s :--- Could not open save.conf ",
				me.name,parv[0]);
			return -1;
  		}	
	  i = 0;

  	  while (conf_items[i].name)
		{
			if(conf_items[i].type == CT_DESC) 
				fprintf(confile,"\n# %s\n",conf_items[i].name);
			else if(conf_items[i].flags & CF_DEFINED)
				{
					fprintf(confile,"%s\t %s\n", conf_items[i].name, conf_item_str(i));
				}
		  ++i;
		}	  
	fprintf(confile,"# End of save.dconf\n");
	  fclose(confile);

	  if(IsService(sptr))
		sendto_serv_butone(cptr, ":%s DCONF SAVE", parv[0]);
	  else
  		sendto_one(sptr, ":%s NOTICE %s :--- File saved",		
			me.name,parv[0]);	  		
	}
  else 
	sendto_one(sptr, ":%s NOTICE %s :Usage DCONF < REHASH | SAVE | LIST [mask] | SET option value] >",
	  me.name, parv[0]);
	  
  return 0;
}
