/*
 * This file is part of blabouncer (https://www.blatech.co.uk/l_bratch/blabouncer).
 * Copyright (C) 2019 Luke Bratch <luke@bratch.co.uk>.
 *
 * Blabouncer 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, version 3.
 *
 * Blabouncer 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 blabouncer. If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

// Sets 'dest' to the value of the configuration option with name
// 'confname' from configuration file 'filename'.
// Returns 1 for success or 0 for error/failure.
int getconfstr(char *confname, char *filename, char* dest) {
  FILE *fp;
  char str[MAXCHAR];
  int found = 0; // Have we found the configuration option?

  // Set strings to zero-length to begin
  dest[0] = '\0';

  // Length of requested configuration option name
  long int namelen = strlen(confname);

  fp = fopen(filename, "r");

  if (fp == NULL) {
    printf("error: could not open configuration file '%s'.\n", filename);
    debugprint(DEBUG_CRIT, "error: could not open configuration file '%s'.\n", filename);
    exit(1);
  }
  // Loop through the whole file, looking for the requested configuration option
  while (fgets(str, MAXCHAR, fp) != NULL) {
    char substr[MAXCHAR];

    // Check if the next character after the length of the requested option
    // name is an equals sign, a space, or a tab
    if (str[namelen] != '=' && str[namelen] != ' ' && str[namelen] != '\t') {
      // If it isn't this can't have been our option
      continue;
    }

    // Copy the number of characters that the requested option name is long
    // to a temporary string
    strncpy(substr, str, namelen);
    substr[namelen] = '\0';

    // If the resulting temporary string contains the requested option name,
    // we have found our configuration option and it is in the current 'str'
    if (strstr(substr, confname)) {
      found = 1;
      break;
    }
  }

  // If we got here, then either we found the configuration option or we ran out of file

  if (!found) {
    debugprint(DEBUG_SOME, "Error reading configuration option '%s', did not find it in configuration file '%s'.\n", confname, filename);
    fclose(fp);
    return 0;
  }

  long int pos = 0;
  char conf[MAXCHAR]; // Temporary string to build configuration value in

  // Starting from the end of the option name, find the position of the start of the configuration value
  // (including its double quotes) by skipping over everything that isn't an equals sign, a space, or a tab
  for (size_t i = namelen; i < strlen(str); i++) {
    if (str[i] == '=' || str[i] == ' ' || str[i] == '\t') {
      continue;
    } else {
      // Record current/final position in string
      pos = i;
      break;
    }
  }

  strncpy(conf, str + pos, strlen(str) - pos - 1); // Copy remainder to new string and lop off the newline
  conf[strlen(str) - pos - 1] = '\0'; // Null terminate

   // Check for start and end quotes
  if (conf[0] != '"' || conf[strlen(conf) - 1] != '"') {
    printf("Error reading configuration option '%s', not enclosed in double quotes in configuration file '%s'!\n", confname, filename);
    debugprint(DEBUG_CRIT, "Error reading configuration option '%s', not enclosed in double quotes in configuration file '%s'!\n", confname, filename);
    exit(1);
  }

  strncpy(dest, conf + 1, strlen(conf) - 2); // Copy result to destination string without quotes
  dest[strlen(conf) - 2] = '\0'; // Null terminate

  debugprint(DEBUG_FULL, "getconfstr(): returning '%s'.\n", dest);

  // Close fine and return success
  fclose(fp);
  return 1;
}

// Returns the value of the configuration option with name
// 'confname' from configuration file 'filename'.
// Sets errno to 0 on success, or ECONFINT if it fails, in which case the return value is undefined.
int getconfint(char *confname, char *filename) {
  errno = 0;
  char result[MAXCHAR];
  if (!getconfstr(confname, filename, result)) {
    debugprint(DEBUG_CRIT, "getconfint(): error getting configuration option '%s' from configuration file '%s'.\n", confname, filename);
    errno = ECONFINT;
  }

  return strtol(result, NULL, 10); // Convert resulting string to an integer, base 10
}

// Create the default configuration file.
// Return 1 on success, 0 on failure.
int createconfigfile(char *filename) {
  char *dirtmp;
  char *dir;
  dirtmp = strdup(filename);
  dir = strdup(dirname(dirtmp));

  // Make sure the parent directory exists
  struct stat st = {0};
  if (stat(dir, &st) == -1) {
    if (mkdir(dir, 0700)) {
      printf("Error creating config directory '%s'.\n", dir);
      debugprint(DEBUG_CRIT, "Error creating config directory '%s'.\n", dir);
      exit(1);
    } else {
      printf("Created config directory '%s'.\n", dir);
    }
  }

  FILE *fp;

  fp = fopen(filename, "a");

  if (fp == NULL) {
    printf("error: could not open default configuration file '%s' for writing.\n", filename);
    debugprint(DEBUG_CRIT, "error: could not open default configuration file '%s' for writing.\n", filename);
    exit(1);
  }

  // Prepare the string
  char *string =
  "# blabouncer configuration file\n"
  "#\n"
  "# Entries must be in the form:\n"
  "# option name, space, equals sign, space, double quote, option value, double quote\n"
  "# e.g.\n"
  "# realname = \"Mr Bla Bouncer\"\n"
  "#\n"
  "# Shell expansion is not supported, so do not try and specify e.g.\n"
  "# \"~/.blabouncer/\" or \"$HOME/.blabouncer/\", instead use \"/home/foo/.blabouncer\"\n"
  "#\n"
  "# Some settings can be reloaded at runtime, please refer to README for details.\n"
  "\n"
  "nick = \"blabounce\"\n"
  "nick2 = \"bbounce2\"\n"
  "nick3 = \"bbounce3\"\n"
  "username = \"bounceusr\"\n"
  "realname = \"Mr Bla Bouncer\"\n"
  "\n"
  "# Channels to automatically join (comma-separated list, defaults to none)\n"
  "# Put channel keywords/passwords after channel names following a space.\n"
  "#channels = \"#blabouncer keyword,#test\"\n"
  "\n"
  "# Auto replay mode upon a bouncer client connecting\n"
  "# \"none\" = Don't auto replay\n"
  "# \"time\" = Always send the last \"replayseconds\" worth of logs\n"
  "# \"lastspoke\" = All messages since your current nick last spoke\n"
  "# \"noclients\" = All messages since you last had no clients connected\n"
  "# \"lastchange\" = All messages since your last client connection/disconnection\n"
  "# \"perclient\" = All messages since the current client last disconnected (see README)\n"
  "replaymode = \"time\"\n"
  "\n"
  "# How many seconds of replay log should be sent to connecting clients if replaymode = \"time\"\n"
  "replayseconds = \"600\"\n"
  "\n"
  "# Connect password clients must provided to connect\n"
  "password = \"bananas\"\n"
  "\n"
  "# Port the bouncer should listen on\n"
  "clientport = \"1234\"\n"
  "\n"
  "# Enable TLS for clients connecting to the bouncer (\"1\" for yes or \"0\" for no)\n"
  "# If \"0\" then certfile and keyfile need not be set\n"
  "clienttls = \"1\"\n"
  "\n"
  "# Enable TLS for the bouncer connecting to the IRC server (\"1\" for yes or \"0\" for no)\n"
  "servertls = \"1\"\n"
  "\n"
  "# Real IRC server the bouncer connects to\n"
  "ircserver = \"irc.blatech.net\"\n"
  "\n"
  "# Real IRC server port\n"
  "ircserverport = \"6697\"\n"
  "\n"
  "# Real IRC server password\n"
  "#ircserverpassword = \"apples\"\n"
  "\n"
  "# Command to send to the server upon completing registration (e.g. a NickServ password)\n"
  "#connectcommand \"PRIVMSG NickServ IDENTIFY bananas\"\n"
  "\n"
  "# Base directory (defaults to $HOME/.blabouncer/)\n"
  "# Things such as the logs directory will be placed below this\n"
  "#basedir = \"/home/foo/.blabouncer/\"\n"
  "\n"
  "# Certificate file (defaults to <basedir>/cert.pem)\n"
  "# If clienttls = \"0\" then this need not be set\n"
  "#certfile = \"/home/foo/.blabouncer/cert.pem\"\n"
  "\n"
  "# Certificate key file (defaults to <basedir>/key.pem)\n"
  "# If clienttls = \"0\" then this need not be set\n"
  "#keyfile = \"/home/foo/.blabouncer/key.pem\"\n"
  "\n"
  "# Enable logging (\"1\" for yes or \"0\" for no)\n"
  "# Logs go to basedir/logs/ with one file per channel/nick\n"
  "logging = \"1\"\n"
  "\n"
  "# Enable replay logging (\"1\" for yes or \"0\" for no)\n"
  "# Replay log goes to basedir/replay.log\n"
  "replaylogging = \"1\"\n"
  "\n"
  "# Debug verbosity (\"0\" for critical only, \"1\" for some extra info, \"2\" for full debug mode)\n"
  "# (All output goes to <basedir>/debug.txt)\n"
  "debug = \"2\"\n"
  "\n"
  "# Number of debug logs to keep\n"
  "debugkeep = \"5\"\n";

  // Write complete string to file
  if ((fprintf(fp, "%s", string)) < 0) {
    debugprint(DEBUG_CRIT, "error: could not write to replay log file.\n");
  }

  fclose(fp);
  free(dirtmp);
  free(dir);
  return 0;
}