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


static char* resolve_listen(const char *hostname, const char *port) {

/* Need room in the strcat for \0 and :
 * the format in the socket unit file is hostname:port */
    char *conn = (char*)malloc(strlen(hostname)+strlen(port)+2);
    strcpy(conn, hostname);
    strcat(conn, ":");
    strcat(conn, port);

    return conn;

}


static int get_listen_from_conf(const char *filename, char **listen) {

  config_t config;
  config_setting_t *setting, *addr;
  const char *hostname, *port;
  int len = 0;

/* look up the listen stanzas in the config file so these
 * can be used in the socket file generated */

  config_init(&config);
  if (config_read_file(&config, filename) == CONFIG_FALSE) {
  /* we don't care if file is missing, skip it */
       if (config_error_line(&config) != 0) {
       fprintf(stderr, "%s:%d:%s\n",
                filename,
                config_error_line(&config),
                config_error_text(&config));
        return -1;
        }
  } else {
   setting = config_lookup(&config, "listen");
    if (setting) {
      len = config_setting_length(setting);
      for (int i = 0; i < len; i++) {
        addr = config_setting_get_elem(setting, i);
        if (! (config_setting_lookup_string(addr, "host", &hostname) &&
               config_setting_lookup_string(addr, "port", &port))) {
          fprintf(stderr,
            "line %d:Incomplete specification (hostname and port required)\n",
            config_setting_source_line(addr));
            return -1;
         } else {

        listen[i] = malloc(strlen(resolve_listen(hostname, port)));
        strcpy(listen[i], resolve_listen(hostname, port));
        }
      }
    }
  }

  return len;

}

static int write_socket_unit(FILE *socket, char **listen, int num_addr, const char *source) {

   fprintf(socket,
        "# Automatically generated by systemd-sslh-generator\n\n"
        "[Unit]\n"
        "Before=sslh.service\n"
        "SourcePath=%s\n"
        "Documentation=man:sslh(8) man:systemd-sslh-generator(8)\n\n"
        "[Socket]\n"
        "FreeBind=true\n",
        source);

        for (int i = 0; i < num_addr; i++) {
            fprintf(socket, "ListenStream=%s\n", listen[i]);
        }

return 0;
}

static int gen_sslh_config(char *runtime_unit_dir) {

  char *sslh_conf;
  int num_addr;
  FILE *config;
  char **listen;
  FILE *runtime_conf_fd = stdout;
  const char *unit_file;

/* There are two default locations so check both with first given preference */
  sslh_conf = "/etc/sslh.cfg";

  config = fopen(sslh_conf, "r");
  if (config  == NULL) {
    sslh_conf="/etc/sslh/sslh.cfg";
     config = fopen(sslh_conf, "r");
    if (config == NULL) {
      return -1;
    }
  }

  fclose(config);


  num_addr = get_listen_from_conf(sslh_conf, listen);
  if (num_addr < 0)
    return -1;

/* If this is run by systemd directly write to the location told to
 * otherwise write to standard out so that it's trivial to check what
 * will be written */
   if (runtime_unit_dir != "") {
   unit_file = "/sslh.socket";
   size_t uf_len = strlen(unit_file);
   size_t runtime_len = strlen(runtime_unit_dir) + uf_len + 1;
   char *runtime_conf = malloc(runtime_len);
       strcpy(runtime_conf, runtime_unit_dir);
       strcat(runtime_conf, unit_file);
       runtime_conf_fd = fopen(runtime_conf, "w");
   }


 return write_socket_unit(runtime_conf_fd, listen, num_addr, sslh_conf);

}

int main(int argc, char *argv[]){

        int r = 0;
        int k;
        char *runtime_unit_dest = "";

        if (argc > 1 && (argc != 4) ) {
                printf("This program takes three or no arguments.\n");
                return -1;
        }

        if (argc  > 1)
            runtime_unit_dest = argv[1];

        k = gen_sslh_config(runtime_unit_dest);
        if (k < 0)
          r = k;

       return r < 0 ? -1 : 0;

}


