/**********************************************************************
 * PTlink IRC Services is (C) CopyRight PTlink IRC Software 1999-2004 *
 *                     http://software.pt-link.net                    *
 * This program is distributed under GNU Public License               *
 * Please read the file COPYING for copyright information.            *
 **********************************************************************  

  Description: dynamic configuration support module

 *  $Id: dbconf.c,v 1.8 2005/10/18 16:25:06 jpinto Exp $
*/

#include "module.h"
#include "my_sql.h"
#include "my_sql.h"
#define DBCONF
#include "dbconf.h"

SVS_Module mod_info =
 /* module, version, description */
{"dbconf", "1.0",  "dbconf support module" };

#define DB_VERSION	1

/** functionsand events we require **/
/* void (*FunctionPointer)(void);*/
int mysql_connection;

MOD_REQUIRES
  MOD_FUNC(mysql_connection)
MOD_END

/** function and events we provide **/
/* void my_function(void); */
int dbconf_cmd_line(int argc, char **argv);

MOD_PROVIDES
  DBCONF_FUNCTIONS
  MOD_FUNC(dbconf_cmd_line)
MOD_END

/** Internal functions declaration **/
/* void internal_function(void); */
    
/** Local variables **/
/* int my_local_variable; */
int dc_log;
    
/** load code **/
int mod_load(void)
{
  dc_log = log_open(mod_info.name, mod_info.name);

  if(dc_log<0)
  {
    errlog("Could not open dbconf log file!");
    return -1;
  }
  
  if(sql_check_inst_upgrade(mod_info.name, DB_VERSION, NULL) < 0)
    return -1;

  return 0;
}

/** unload code **/ 
void mod_unload(void) 
{
  return;
}

/** internal functions implementation starts here **/
/*
 * dbconf_get_or_build
 * For each item of the dbitems array
 *   If the value exists on the db
 *		update its description
 *		set the value pointer if found
 * else
 *		try to add item, returns -1 if fails
 * returns:
 *		-1 , error adding item
 *		>=0	number of items added to the db
 */
int dbconf_get_or_build(char *module, dbConfItem* dbitems)
{  
  int new_item = 0;  
  dbConfItem* item = dbitems;

  while(item && item->name)
  {
    if(sql_singlequery("SELECT value FROM dbconf WHERE module=%s AND name=%s"
      " ORDER BY module, name",
        sql_str(module), sql_str(item->name)) > 0)
    {
      /* read the value */
      if(!strcmp(item->type, "str") || !strcmp(item->type, "word"))
      {
        FREE(*(char **)item->vptr);
        *(char **)item->vptr = sql_field(0) ? strdup(sql_field(0)) : NULL;
      } 
      else 
      if(!strcmp(item->type,"int") && sql_field_i(0))
        *(int*) item->vptr = sql_field_i(0);
      else 
      if(!strcmp(item->type,"time") && sql_field(0))
      {
        if(ftime_str(sql_field(0)) == -1)
        {
          errlog("Invalid time value on  %s.%s",
            module, item->name);
          return -1;
        } 
        *(int*) item->vptr = ftime_str(sql_field(0));
      }
      else
      if(!strcmp(item->type,"switch") && sql_field(0))
        *(int*) item->vptr = (!strcasecmp(sql_field(0),"on")); 
        
      /* update type and description */
      sql_execute("UPDATE dbconf SET stype=%s, ddesc=%s"
        " WHERE module=%s AND name=%s",
        sql_str(item->type),
        sql_str(item->desc), sql_str(module), sql_str(item->name));    
    }
    else
    {
      sqlb_init("dbconf");
      sqlb_add_str("module", module);
      sqlb_add_str("name", item->name);      
      sqlb_add_str("stype", item->type);
      sqlb_add_str("ddesc",	item->desc);
      sqlb_add_str("optional", item->optional);
      sqlb_add_str("configured", "n");
      sqlb_add_str("value", item->def);      
      if(sql_execute(sqlb_insert()) < 0)
      {
        errlog("Error adding dbconf item %s!", item->name);
        return -1;
      }
      if(!strcmp(item->type, "str") || !strcmp(item->type, "word"))
      {
        FREE(*(char **)item->vptr);
        *(char **)item->vptr = item->def ? strdup(item->def): NULL;
      } 
      else 
      if(!strcmp(item->type, "int") && item->def)
        *(int*) item->vptr = atoi(item->def);
      else
      if(!strcmp(item->type, "switch"))
        *(int*) item->vptr = (!strcasecmp(item->def,"on"));
      else
      if(!strcmp(item->type,"time") && item->def)
      {
        if(ftime_str(item->def) == -1)
        {
          errlog("Invalid default time value on  %s.%s",
            module, item->name);
          return -1;
        } 
        *(int*) item->vptr = ftime_str(item->def);
      } 
      ++new_item;
    }
    item++;
  }
  if(new_item)
    stdlog(L_INFO, "Installed %d new configuration item(s)", new_item);
  return new_item;
}

/* change_item
 * Change a dbconf item, the value is validated accoring to the item type
 * Returns:
 * 	0  - Change was successfull
 *	-1 - Item was not found
 *	-2 - Item of type SWITCH but value is not ON/OFF
 *	-3 - Item of type TIME but value is not a time
 *	-4 - Item of type WORD but value is not a word
 *	-5 - Item of type INT but value is not an positive integer
 *	-6 - Unable to unset item, is not optional
 */ 
static int change_item(char *item, char *value)
{
  MYSQL_RES *res;
  MYSQL_ROW row;
  int error = 0;
  res = sql_query("SELECT module,name,stype,optional FROM dbconf WHERE "
    "CONCAT(module,'.', name) = %s", sql_str(item));
  if(!res || !(row = sql_next_row(res)))
    error = -1;
  else
  if((value == NULL) && (*row[3]=='n')) /* this item is not optional */
    error = -6;
  else
  if(strcasecmp(row[2],"switch") == 0) /* "SWITCH" item */
  {
    if(strcasecmp(value,"on") && strcasecmp(value,"off"))
     error = -2;
  } else
  if(strcasecmp(row[2], "time") == 0) /* "TIME" item */
  {
    if(ftime_str(value) == -1)
     error = -3;
  } else
  if((strcasecmp(row[2], "word") == 0) && value) /* "WORD" item */
  {
    if(strchr(value, ' '))
      error = -4;
  } else
  if(strcasecmp(row[2], "int") == 0) /* "INT" item */
  {
    if(!is_posint(value))
     error = -5;
  }

  sql_free(res);
  if(error)
    return error;
  if(sql_execute("UPDATE dbconf SET value=%s "
    "WHERE CONCAT(module,'.',name)=%s", sql_str(value), sql_str(item)) < 0)
      return -6;
      
  return 0;
}

int dbconf_cmd_line(int argc, char **argv)
{
  const char* usage = "Usage:\n"
    "ircsvs conf list [pattern]\n"
    "ircsvs conf export [pattern]\n"
    "ircsvs conf set module.setting value\n"
    "ircsvs conf unset module.setting\n";
  char* cmd;
  char buf[128];
  
  if(argc<1)
  {
    printf("%s", usage);
    return -1;
  }
  cmd = argv[0];  
  if((strcasecmp(cmd, "list") == 0) || (strcasecmp(cmd, "export") == 0))
  {
    MYSQL_RES *res;
    MYSQL_ROW row;
    char *where;
    int is_export = (strcasecmp(cmd, "export") == 0);
    
    if(argc>1)
    {
      char buf2[128];
      snprintf(buf2, sizeof(buf2), "%%%s%%", argv[1]);
      snprintf(buf, sizeof(buf), " WHERE CONCAT(module,'.',name) LIKE %s", 
        sql_str(buf2));
      where = buf;
    }
    else 
      where = "";
    res = sql_query("SELECT module, name, value, ddesc, stype, optional"
      " FROM dbconf %s", where); 
      
    printf("####### Configuration list #######\n");
    while((row = sql_next_row(res)))
    {
      char *line;
      line = row[3];
      /* show each line from the ddesc field prefixed with # */
      while(line)
      {
        char *p = line;
        char *c = strchr(line,'\n');
        if(c)
        {
          *c = '\0';
          line = c+1;
        } else line = NULL;
        printf("# %s\n", p);
      }
      if(strcmp(row[4],"switch") == 0)
        printf("# This is a switch option, possible values are On or Off\n");
      else
      if(strcmp(row[4],"time") == 0)
        printf("# Time value [m=minutes;h=hours;d=days;M=months,Y=years]\n");
      else
      if(*row[5] == 'y')
        printf("# This setting is optional, you can unset to disable\n");
      if(is_export)
      {
        if(strcmp(row[4],"word") && strcmp(row[4],"str"))
          printf("./ircsvs conf set %s.%s %s", row[0], row[1], row[2] ? row[2] : "NULL");
        else
        if(row[2])
          printf("./ircsvs conf set %s.%s \"%s\"", row[0], row[1], row[2]);
        else
          printf("./ircsvs conf unset %s.%s", row[0], row[1]);
      }
      else
      {
        if(strcmp(row[4],"word") && strcmp(row[4],"str"))
          printf("%s.%s = %s", row[0], row[1], row[2] ? row[2] : "NULL");
          else
        if(row[2])
          printf("%s.%s = \"%s\"", row[0], row[1], row[2]);
        else
          printf("%s.%s = *NOT SET*", row[0], row[1]);
      }
      printf("\n\n");
    }
    printf("##################################\n");
    sql_free(res);
  } else
  if((strcasecmp(cmd, "set") == 0) || (strcasecmp(cmd, "unset") == 0))  
  {
    int r;
    const char *msg = NULL;
    int unset = (strcasecmp(cmd, "unset") == 0);
    if((!unset && (argc < 3)) || !strchr(argv[1], '.'))
    {
      printf("%s", usage);
      return -1;
    }
    r =  change_item(argv[1], unset ? NULL : argv[2]);
    switch(r) 
    {
      case 0:
        msg = NULL;
        break;
      case -1: 
        msg = "There is no item %s !\n";
         break;
      case -2:
        msg = "Value for %s must be On/Off !\n";
        break;
      case -3:
        msg = "Value for %s must be a time value !\n";
        break;
      case -4:
        msg = "Value for %s must be a word !\n";
        break;
      case -5:
        msg = "Value for %s must be a positive integer !\n";
        break;
      case -6:
        msg = "Value for %s can't be unset, is not an optional setting!\n";
        break;
      default:
        msg = "Unknown error changing %s !\n";
        break;
    }
    /* check for errors */
    if(msg)
    {
      printf(msg, argv[1]);
      return r;
    }
    if(unset)
      printf("%s successfully unset\n", argv[1]);
    else
      printf("%s successfully changed to: %s\n", argv[1], argv[2]);
  }
  return 0;
};

/* dbconf_get
 * For each item of the dbitems array
 *	Get the value from the dbconf table
 *	If the item is missing, report error and return -1
 *	If the value is null and the item is mandatory, report error
 *		and return -2
 * return 0 on success
 */
int dbconf_get(dbConfGet *dbitems)
{
  dbConfGet* item = dbitems;
  char *stype;
  while(item && item->name)
  {
    if(sql_singlequery("SELECT value, stype, optional FROM dbconf WHERE module=%s AND name=%s"
      " ORDER BY module, name",
        sql_str(item->module), sql_str(item->name)) > 0)
    {
      stype = sql_field(1);
      /* read the value */
      if(!strcmp(stype, "str") || !strcmp(stype, "word"))
      {
        FREE(*(char **)item->vptr);
        *(char **)item->vptr = sql_field(0) ? strdup(sql_field(0)) : NULL;
      } 
      else 
      if(!strcmp(stype,"int") && sql_field_i(0))
        *(int*) item->vptr = sql_field_i(0);
      else
      if(!strcmp(stype,"time") && sql_field(0))
      {
        if(ftime_str(sql_field(0)) == -1)
        {
          errlog("Invalid time value on  %s.%s",
            item->module, item->name);
          return -1;
        }
        *(int*) item->vptr = ftime_str(sql_field(0));
      }
      if(!strcmp(stype,"switch") && sql_field(0))
        *(int*) item->vptr = (!strcasecmp(sql_field(0),"on"));   
    } else
    {
      errlog("Unable to find configuratiom item %s.%s", 
        item->module, item->name);
      return -1;
    }
    if((*(sql_field(2)) == 'n') && (item->vptr == NULL))
    {
      errlog("Mandatory item %s.%s is not set!",
        item->module, item->name);
      return -2;
    }
    ++item;
  }
  return 0;
}

/* End of module */


syntax highlighted by Code2HTML, v. 0.9.1