/***************************************************************************
                          configuration.cpp  -  description
                             -------------------
    begin                : wo dec 25 2002
    copyright            : (C) 2002 by Meilof
    email                : meilof@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <poslib/server/server.h>
#include <poslib/server/configuration.h>
 
#include "configuration.h"
#include "lib.h"
#include "zonefile.h"
#include "primary.h"
#include "masterfile.h"

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

/* ---- configuration settings --------------------------------------------- */

bool cmd_logfile;
char i_logfile[PATH_MAX];
char *logfile = i_logfile;
bool logfile_per_day;

char i_confdir[PATH_MAX];
char *confdir = i_confdir;
//char *mfdir = i_mfdir;

char i_datadir[PATH_MAX];
char *datadir = i_datadir;

int max_cache_items;
int resolv_patience;
int max_cname_recursion;

int update_ttl;

bool do_query_logging;
bool omit_ns_records;
bool cache_statistics;

#ifndef _WIN32
bool cmd_username, cmd_groupname;
char username[128], groupname[128];
#endif

stl_slist(addrrange) recursive_allow;
stl_slist(addrrange) recursive_refuse;

int maxtcp;
stl_slist(addrrange) tcp_priority;

stl_list(stl_string) cachefiles;

/* ---- end: configuration settings ----------------------------------------- */

void config_init() {
  poslib_config_init();

  cmd_logfile = false;
  i_logfile[0] = 0;
  logfile_per_day = false;
  i_confdir[0] = 0;
  i_datadir[0] = 0;
  max_cache_items = 1000;
  resolv_patience = 15;
  max_cname_recursion = 10;
  do_query_logging = false;
  omit_ns_records = false;
  cache_statistics = false;
  update_ttl = 600;
#ifndef _WIN32
  cmd_username = false; cmd_groupname = false;
  username[0] = 0; groupname[0] = 0;
#endif

  recursive_allow.clear();
  recursive_refuse.clear();

  maxtcp = -1;
  tcp_priority.clear();
  cachefiles.clear();
}

bool provide_recursion(_addr *a) {
  if (in_addrrange_list(recursive_allow, a) && !in_addrrange_list(recursive_refuse, a))
    return true;
  else
    return false;
}

bool our_allow_tcp_connection(_addr *a, int ntcp) {
  return ( (maxtcp == -1 || ntcp < maxtcp) &&
           n_threads < max_threads ) || in_addrrange_list(tcp_priority, a);
}

void load_all_zone_files() {
  if (confdir[0] == 0) throw PException("You should specify a configdir before setting AllZoneFiles");

  DIR *dir;
  struct dirent *ent;
  stl_string path;
  domainname dom;

  if ((dir = opendir(confdir)) == NULL) throw PException("Could not open configdir for reading");
  while ( (ent = readdir(dir)) != NULL ) {
    path = stl_string(confdir) + ent->d_name;
    try {
      if (is_zonefile(ent->d_name, dom)) {
        load_zone_file(dom, path.c_str(), false);
      } else if (is_masterfile(ent->d_name, dom)) {
        PrimaryZone *z = new PrimaryZone();
        add_authoritative_zone(dom, z);
        z->feed_setting("file", path.c_str());
      }
    } catch (PException p) {
      throw PException(true, "Zonefile %s: %s", ent->d_name, p.message);
    }
  }
  closedir(dir);
}  

bool conf_feed_setting(const char *set, const char *value) {
  int l;
  _addr a;
  addrrange range;
  domainname dom;
  stl_string path;
  if (strcmpi(set, "mfdir") == 0) {
    pos_log(context_conf, log_warning, "The mfdir command is obsolete. Use configdir instead.");
    goto _confdir;
  } else if (strcmpi(set, "confdir") == 0 || strcmpi(set, "configdir") == 0) {
_confdir:
    if (strlen(value) + 2 >= PATH_MAX) printf("Master file path too long");
    strcpy(confdir, value);
    l = strlen(confdir) - 1;
    if (confdir[l] != SEP) { confdir[l+1] = SEP; confdir[l+2] = '\0'; }
  } else if (strcmpi(set, "max_cache_items") == 0) {
    max_cache_items = txt_to_int(value);
  } else if (strcmpi(set, "resolv_patience") == 0) {
    resolv_patience = txt_to_int(value);
  } else if (strcmpi(set, "max_cname_recursion") == 0) {
    max_cname_recursion = txt_to_int(value);
  } else if (strcmpi(set, "max_threads") == 0) {
    max_threads = txt_to_int(value);
  } else if (strcmpi(set, "max_tcp_connections") == 0) {
    maxtcp = txt_to_negint(value);
  } else if (strcmpi(set, "tcp_priority") == 0) {
    txt_to_addrrange(range.range, value);
    tcp_priority.push_front(range);
  } else if (strcmpi(set, "loadmodule") == 0) {
    load_library(value);
  } else if (strcmpi(set, "allow_recursion") == 0) {
    txt_to_addrrange(range.range, value);
    recursive_allow.push_front(range);
  } else if (strcmpi(set, "refuse_recursion") == 0) {
    txt_to_addrrange(range.range, value);
    recursive_refuse.push_front(range);
  } else if (strcmpi(set, "datadir") == 0) {
    if (strlen(value) + 1 >= PATH_MAX) throw PException("Data dir name too long");
    strcpy(datadir, value);
    if (datadir[strlen(datadir)-1] != SEP) {
      datadir[strlen(datadir)+1] = '\0';
      datadir[strlen(datadir)] = SEP;
    }
  } else if (strcmpi(set, "logfile") == 0) {
    if (cmd_logfile) return true;
    if (strlen(value) >= PATH_MAX) throw PException("Log file name too long");
    strcpy(logfile, value);
  } else if (strcmpi(set, "logfile_per_day") == 0) {
    logfile_per_day = txt_to_bool(value);
#ifndef _WIN32
  } else if (strcmpi(set, "user") == 0) {
    if (cmd_username) return true;
    if (strlen(value) >= 127) throw PException("User name too long");
    strcpy(username, value);
  } else if (strcmpi(set, "group") == 0) {
    if (cmd_groupname) return true;
    if (strlen(value) >= 127) throw PException("User name too long");
    strcpy(groupname, value);
#endif
  } else if (strcmpi(set, "listen") == 0) {
    try {
      if (strncmpi(value, "tcp/", 4) == 0) {
        txt_to_addr(&a, value + 4, DNS_PORT, false);
        servers.push_front(ServerSocket(ss_tcp, tcpcreateserver(&a)));
      } else if (strncmpi(value, "udp/", 4) == 0) {
        txt_to_addr(&a, value + 4, DNS_PORT, false);
        servers.push_front(ServerSocket(ss_udp, udpcreateserver(&a)));
      } else if (strncmpi(value, "both/", 5) == 0) {
        txt_to_addr(&a, value + 5, DNS_PORT, false);
        servers.push_front(ServerSocket(ss_udp, udpcreateserver(&a)));
        servers.push_front(ServerSocket(ss_tcp, tcpcreateserver(&a)));
      } else {
        txt_to_addr(&a, value, DNS_PORT, false);
        servers.push_front(ServerSocket(ss_udp, udpcreateserver(&a)));
        servers.push_front(ServerSocket(ss_tcp, tcpcreateserver(&a)));
      }
    } catch (PException p) {
      throw PException(true, "Could not listen to network interface %s: %s", value, p.message);
    }
  } else if (strcmpi(set, "libdir") == 0) {
    add_libdir(value);
  } else if (strcmpi(set, "do_query_logging") == 0) {
    do_query_logging = txt_to_bool(value);
  } else if (strcmpi(set, "omit_ns_records") == 0) {
    omit_ns_records = txt_to_bool(value);
  } else if (strcmpi(set, "cache_statistics") == 0) {
    cache_statistics = txt_to_bool(value);
  } else if (strcmpi(set, "zone_file") == 0 || strcmpi(set, "zonefile") == 0) {
    if (confdir[0] == 0) throw PException("You should specify a configdir before loading any zone files");
    path = stl_string(confdir) + value;
    if (is_zonefile(value, dom)) {
      load_zone_file(dom, path.c_str(), false);
    } else if (is_masterfile(value, dom)) {
      PrimaryZone *z = new PrimaryZone();
      add_authoritative_zone(dom, z);
      z->feed_setting("file", path.c_str());      
    } else {
      throw PException(true, "%s cannot be recognized as a zone or master file. Make sure it is either in zn.<zone>, <zone>.znf, db.<zone> or <zone>.znf format.", value);
    }
  } else if (strcmpi(set, "all_zone_files") == 0 || strcmpi(set, "allzonefiles") == 0) {
    if (txt_to_bool(value) == true) {
      load_all_zone_files();
    }
  } else if (strcmpi(set, "update_ttl") == 0) {
    update_ttl = txt_to_int(value);
  } else if (strcmpi(set, "initial_cache_file") == 0) {
    cachefiles.push_back(value);
  } else {
    return false;
  }

  return true;
}

void conf_end_setting() {
  addrrange range;
  if (recursive_allow.empty()) {
    txt_to_addrrange(range.range, "any");
    recursive_allow.push_front(range);
  }
  /* load the zone files */
  stl_list(stl_string)::iterator it = cachefiles.begin();
  while (it != cachefiles.end()) {
    if (!strchr(it->c_str(), SEP))
      load_initial_cache((stl_string(confdir) + it->c_str()).c_str());
    else
      load_initial_cache(it->c_str());
    it++;
  }
  add_libdir(plugin_dir);
}

bool recursive_zone_end_setting(void *data, char *name, Zone *z) {
  if (!z->is_placeholder) z->end_setting();
  z->subitems.iterate((iteratorfn)recursive_zone_end_setting, NULL);
  return true;
}


syntax highlighted by Code2HTML, v. 0.9.1