#include "mysql/mysql.h"
#include "auto_qmail.h"
#include "fmt.h"
#include "qsutil.h"
#include "readwrite.h"
#include "scan.h"
#include "str.h"
#include "stralloc.h"
#include "subfd.h"
#include "substdio.h"

#define SQLSERVER "sqlserver"
#define TRIES 3

MYSQL dbh, *mysql;
MYSQL_RES *result;

int connect_mysql() {
  /* if database handle is available AND it pings then there's nothing to do */
  DEBUG_SAY("connect_mysql(): checking existing connection\n");
  if (mysql) if (! mysql_ping(&dbh)) return 1;
  DEBUG_SAY("no connection, must reconnect\n");
  return init_mysql();
}

/* try to connect to database with the username/password from config file     */
/* returns 1 if we were already connected or we connected successfully        */
/* returns 0 if we could not connect                                          */
int init_mysql() {
  int i, tries;
  unsigned long portnum;
  stralloc host = { 0 };
  stralloc user = { 0 };
  stralloc pass = { 0 };
  stralloc name = { 0 };
  stralloc port = { 0 };
  stralloc sock = { 0 };
  /* already used tries AND it has four letters :-) */
  stralloc fois = { 0 };

  DEBUG_SAY("calling mysql_init()\n");
  mysql = mysql_init(&dbh);
  if (! mysql) return 0;
  DEBUG_SAY("mysql_init() returned successfully\n");

  DEBUG_SAY("calling read_config_file()\n");
  if (! read_config_file(&host, &user, &pass, &name, &port, &sock, &fois, &portnum, &tries)) {
    log1("error: without a valid config file we can't access the database!\n");
    return 0;
  }
  DEBUG_SAY("read_config_file() returned successfully\n");

  /* have several goes attempting to the database */
  for (i = 0; i < tries; i++) {
    DEBUG_SAY("earth to mysqld, come in please mysqld\n");
    mysql = mysql_real_connect(&dbh, host.s, user.s, pass.s, name.s, 
                               portnum, sock.s, 0);
    if (mysql) {
#ifndef O_NOT_LOG_CONNECTS
      log3("connected to '", name.s, "' database\n");
#endif
      break;
    }
    log3("error: ", mysql_error(&dbh), "\n");
    sleep(3);
  }

  stralloc_free(&host);
  stralloc_free(&user);
  stralloc_free(&pass);
  stralloc_free(&port);
  stralloc_free(&sock);
  stralloc_free(&fois);

  if (! mysql) {
    log3("giving up on connection to '", name.s, "' database\n");
    stralloc_free(&name);
    return 0;
  }
  stralloc_free(&name);
  return 1;
}

void disconnect_mysql() {
  mysql_close(&dbh);
}

int read_config_file(stralloc *host, stralloc *user, stralloc *pass, stralloc *name, stralloc *port, stralloc *sock, stralloc *fois, unsigned long *portnum, int *tries) {
  int error, i, file, match;
  substdio ss;
  stralloc filename = { 0 };
  stralloc buf = { 0 };
  char inbuf[64];

  /* get the filename of the sqlserver control file */
  DEBUG_SAY("figuring out sqlserver path\n");
  i = str_len(SQLSERVER) + str_len(auto_qmail) + 10;
  if (! stralloc_ready(&filename, i)) return 0;
  if (! stralloc_cats(&filename, auto_qmail)) return 0;
  if (! stralloc_cats(&filename, "/control/")) return 0;
  if (! stralloc_cats(&filename, SQLSERVER)) return 0;
  if (! stralloc_0(&filename)) return 0;
  DEBUG_PUT("sqlserver file is \"");
  DEBUG_PUT(filename.s);
  DEBUG_SAY("\"\n");
 
  DEBUG_SAY("stand by, we're trying the file\n");
  file = open_read(filename.s);
  if (file == -1) {
    log3("error: could not open sqlserver file \"", filename.s, "\"\n");
    stralloc_free(&filename);
    stralloc_free(&buf);
    return 0;
  }

  substdio_fdbuf(&ss, read, file, inbuf, sizeof(inbuf));
  while (getln(&ss, &buf, &match, '\n') != -1) {
    if (! match && ! buf.len) break;
    buf.len--;
    if (! stralloc_0(&buf)) break;
    /* we consider only lines starting with the letters h, s,l,p,d or t */
    switch (buf.s[0]) {
      case 'h': error = getconfig(&buf, "host", host); break;
      case 's':
        if (buf.s[1] == 'e') error = getconfig(&buf, "server", host);
        else error = getconfig(&buf, "socket", sock);
        break;
      case 'l': error = getconfig(&buf, "login", user); break;
      case 'p':
        if (buf.s[1] == 'a') error = getconfig(&buf, "password", pass);
        else error = getconfig(&buf, "port", port);
        break;
      case 'd': error = getconfig(&buf, "db", name); break;
      case 't': error = getconfig(&buf, "tries", fois); break;
      default: continue;
    }
    if (error == -1) continue;
    if (! match) break;
  }

  DEBUG_SAY("closing configuration file\n");
  close(file);
  stralloc_free(&filename);
  stralloc_free(&buf);

  /* if a port is provided it must be a valid number */
  if (port->a > 0) {
    DEBUG_SAY("checking validity of provided port number\n");
    i = scan_ulong(port->s, portnum);
    if (! i || port->s[i]) {
      log3("error: bogus port number: \"", port->s, "\"\n");
      return 0;
    }
  }
  else *portnum = 0;

  /* specific number of connection attempts */
  if (fois->a > 0) {
    DEBUG_SAY("checking validity of requested connection attempts\n");
    i = scan_ulong(fois->s, tries);
    if (! i || fois->s[i]) {
      log3("warning: bogus connection attempts number: \"", fois->s, "\"\n");
      if (stralloc_ready(fois, FMT_ULONG)) {
        fois->s[fmt_ulong(fois->s, TRIES)] = '\0';
        log3("warning: using default ", fois->s, "\n");
      }
      *tries = TRIES;
    }
    else {
      if (*tries == 0) {
        log1("error: we must make at least one connection attempt\n");
        return 0;
      }
      DEBUG_PUT("we will try ");
      if (*tries == 1) DEBUG_PUT("once (only) ");
      else {
        DEBUG_PUT(fois->s);
        DEBUG_PUT(" times ");
      }
      DEBUG_SAY("to connect to the database\n");
    }
  }
  else *tries = TRIES;

  /* we must have at least a username */
  if (! user->a) {
    log3("error: no username provided in \"", filename.s, "\"\n");
    return 0;
  }

  if (host->a) {
    DEBUG_PUT("host is \"");
    DEBUG_PUT(host->s);
    DEBUG_SAY("\"\n");
  }

  if (port->a) {
    if (sock->a) {
      log3("error: you can't specify a port number AND a socket\n");
      return 0;
    }
    DEBUG_PUT("port is ");
    DEBUG_PUT(port->s);
    DEBUG_SAY("\n");
  }

  DEBUG_PUT("user is \"");
  DEBUG_PUT(user->s);
  DEBUG_SAY("\"\n");

  if (pass->a) DEBUG_SAY("a password was provided\n");

  return 1;
}

/* read lines in the config file and grok entries of the form "x=y" or "x y"  */
/* got is the line in the file, expected is the parmeter we're checking for   */
/* and store is a stralloc to put the result into if it was found             */
int getconfig(stralloc *got, char *expected, stralloc *store) {
  char *value, *x;

  x = got->s + str_len(expected);
  value = x + 1;
  /* we got a meaningful value */
  if (! stralloc_starts(&got, expected) && (*x == ' ' || *x == '\t' || *x == '=') && str_len(value) > 0) {
    if (! stralloc_ready(store, str_len(value) + 1)) return -1;
    if (! stralloc_cats(store, value)) return -1;
    if (! stralloc_0(store)) return -1;
    return 0;
  }
  /* oh dear, the line we got didn't contain what we expected it to */
  log3("error: reading sqlserver file, expected \"", expected, "\" but got \"");
  log2(got, "\"\n");
  /* of course, we might have been expecting the wrong thing... */
  return -1;
}


syntax highlighted by Code2HTML, v. 0.9.1