/*
 * bot.c
 * (C) Peter Salanki 2002
 * This program is copyright, and covered by the Gnu Public License.
 * The Natasha bot.
 * sorcer@linux.se
 */

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <pthread.h>
#include <pcre.h>

#include "bottypes.h"
#include "settings.h"
#include "globals.h"
#include "modules/irchandlers/servtab.h"

int nextcheck;
int lines = 0;
int rejoint = 0;
int reloadmsgmod = 0;
int reloadircmod = 0;
short connectarms = 1;
#ifdef SAFEHOME
#ifdef MAIN
int nextsecure;
#endif
char homekey[KEYLEN];
#endif
char compiledate[] = __DATE__ " " __TIME__;

MYSQL mysql, *sock;
MYSQL_RES *res;
MYSQL_ROW row;

MYSQL logmysql, *logsock;
MYSQL_RES *logres;
MYSQL_ROW logrow;

/* Global mutexes */
pthread_mutex_t mysqlmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t logmysqlmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalusermutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalchannelmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalchanusermutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalacumutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalmodmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalarmmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalbanmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t globalmutex=PTHREAD_MUTEX_INITIALIZER;

/*
 * The heads and tails of the linked lists used for all the databases.
 */

struct arm *lastarm = NULL;
struct arm *firstarm = NULL;

struct module *firstmodule = NULL;
struct module *lastmodule = NULL;

struct activeuser *lastauser = NULL;
struct activeuser *firstauser = NULL;

struct channel *firstchan = NULL;
struct channel *lastchan = NULL;

struct chanuser *firstchanuser = NULL;
struct chanuser *lastchanuser = NULL;

struct activechanuser *firstacu = NULL;
struct activechanuser *lastacu = NULL;

void cquit(int);
void seg(int);
void sigusr1(int);
void stresstest(int);

/*
 * OK, Useful functions.
 */

char *STR_replace_c (char *source, char *old, char *new, char *dest) {
  int         i=0, j=0;
  int         lold, lnew, lsource;
  int         tempwork=0;
  int         templen=1024;
  char        *tempdest;
     
    /* create a temp work area if source = dest */
    if (source == dest) {
        tempwork = 1;
        tempdest = calloc(templen, sizeof(char));
    }
    else {
      /* since dest is different from source, do work in dest */
        tempdest = dest;
    }     
    lold = strlen(old);
    lnew = strlen(new);
    lsource = strlen (source);
    while (i < (lsource - lold + 1)) {
        /* if temparea used and too small, increase its size */
        if (tempwork && ((j + lnew) >= templen)) {
            templen += 256;
            tempdest = realloc(tempdest, templen * sizeof(char));
        }
        if (strncmp (&source[i], old, lold)) {
            tempdest[j++] = source[i++];
        }
        else {
            strcpy (&tempdest[j], new);
            j = j + lnew;
            i = i + lold;
        }
    }     
    /* if temp work area used and too small, increase its size */
    if (tempwork && ((j + strlen(&source[i])) >= templen)) {
        templen += 256;
        tempdest = realloc(tempdest, templen * sizeof(char));
    }
    tempdest[j] = 0;
    strcat (&tempdest[j], &source[i]);     
    /* if a temp work area was used, copy to final dest */   
    if (tempwork && ((j + strlen(&source[i])) >= templen)) {
        templen += 256;
        tempdest = realloc(tempdest, templen * sizeof(char));
    }
    tempdest[j] = 0;
    strcat (&tempdest[j], &source[i]);     /* if a temp work area was used, copy to final dest */
    if (tempwork) {
        strcpy(dest, tempdest);
        free(tempdest);
    }
    return (dest);
}

int regex_match(char *regex, char *string) {
  pcre *re;
  const char *error;
  int erroffset;
  int rc;

  /* Compile the regular expression in the first argument */

  re = pcre_compile(regex, 0, &error, &erroffset, NULL);

  /* Compilation failed: print the error message and exit */

  if (re == NULL)
    {
      printf("PCRE compilation failed at offset %d: %s\n", erroffset, error);
      return 0;
    }
 
  /* Compilation succeeded: match the subject in the second argument */

  rc = pcre_exec(re, NULL, string, (int)strlen(string), 0, 0, NULL, 0);

  if (rc < 0) return 0;
 
  return 1;
}

void dbconnect (void) {
  if (!(sock = mysql_real_connect(&mysql,MYSQL_SERVER,MYSQL_USER,MYSQL_PASS,MYSQL_DB,MYSQL_PORT,NULL,0)))
    {
      fprintf(stderr,"Couldn't connect to MySQL server!\n%s\n\n", mysql_error(&mysql));
      perror("");     
      exit(1);
    }
}

void logdbconnect (void) {
  if (!(logsock = mysql_real_connect(&logmysql,MYSQL_LOGSERVER,MYSQL_LOGUSER,MYSQL_LOGPASS,MYSQL_LOGDB,LOGMYSQL_PORT,NULL,0)))
    {
      fprintf(stderr,"Couldn't connect to Log-MySQL server!\n%s\n\n", mysql_error(&logmysql));
      perror("");     
      exit(1);
    }
}

void dbquery (char *query) {
  char tmp[512]= "";

  pthread_mutex_lock(&mysqlmutex); // Lock mutex

  if(mysql_query(sock, query))
    {
#ifdef DEBUG 
     printf("Query failed (%s)\n", mysql_error(sock));
#endif

      snprintf(tmp, 512, "%c4Warning:%c Query failed (%s). Query: %s", COLORS, COLORS, mysql_error(sock), query);
      privmsg(HOMECHAN, tmp);

      if(strcmp(mysql_error(sock), "MySQL server has gone away") == 0 || strcmp(mysql_error(sock), "Lost connection to MySQL server during query") == 0) {
	dbconnect();
	usleep(200);
	mysql_query(sock, query);
	if(!mysql_query(sock, query)) res = mysql_store_result(sock);
      }
      
    } else res = mysql_store_result(sock); 
}

void logdbquery (char *query) {
  char tmp[512]= "";

  pthread_mutex_lock(&logmysqlmutex); // Lock mutex

  if(mysql_query(logsock, query))
    {
#ifdef DEBUG 
     printf("Query failed (%s)\n", mysql_error(logsock));
#endif

      snprintf(tmp, 512, "%c4Warning:%c Query failed (%s). Query: %s", COLORS, COLORS, mysql_error(logsock), query);
      privmsg(HOMECHAN, tmp);

      if(strcmp(mysql_error(logsock), "MySQL server has gone away") == 0 || strcmp(mysql_error(logsock), "Lost connection to MySQL server during query") == 0) {
	logdbconnect();
	usleep(200);
	mysql_query(logsock, query);
	if(!mysql_query(logsock, query)) logres = mysql_store_result(logsock);
      }
      
    } else logres = mysql_store_result(logsock); 
}

char *escapequery (char *query) {
  char *escaped = malloc(2*strlen(query));
  
  mysql_real_escape_string(&mysql, escaped, query, strlen(query));
  
  return escaped;
}

void loadmodulesinconf (void) {
  char path[100]; 
  FILE *pFile;   
  
  pFile = fopen ("modules.conf","r");

  if(pFile == NULL) {
    printf("Could not open modules.conf, Natasha aborting.\n");
    exit(0);
  }

  while (fscanf (pFile, "%s\n", path) != EOF) if(path[0] != '#' && path[0] != '\0') loadmodule(path, 1);
  //  while (fgets(path, 100, pFile) != EOF) if(path[0] != '#' && path[0] != '\0') loadmodule(path, 1);
  fclose (pFile);
}

char *loaddatafromfile (char *file) {
  FILE *pFile;   
  static char buffer[513];
  
  pFile = fopen (file,"r");
  
  if(pFile == NULL) {
    printf("Could not open: %s.\n", file);
    return &(buffer[1]);
  }

  fgets (buffer , 512 , pFile);
  fclose (pFile);

  return &(buffer[0]);
}

int writedatatofile (char *file, char *data) {
  FILE *pFile; 

  pFile = fopen (file, "w");
  
  if(pFile == NULL) {
    printf("Could not open: %s.\n", file);
    return 0;
  }
  
  fputs (data, pFile);
  fclose (pFile);

  return 1;
}

#ifdef MAIN
#ifdef SAFEHOME
void securehome (void) {
  char msg[512];
  char newkey[KEYLEN];
  
  srand (time(NULL)); /* Init random number generator */
  
  snprintf(msg, 512, "MODE " HOMECHAN " -k %s\n\r", homekey); /* Remove old key */
  putserver(msg, firstarm);
  
  snprintf(newkey, KEYLEN, "SE%iE", rand()); /* Generate new key */
  
  snprintf(msg, 512, "MODE " HOMECHAN " +k %s\n\r", newkey); /* Set new key */
  putserver(msg, firstarm);

  printf("Home channel secured, new key set to: %s\n", newkey);
}
#endif
#endif

int countargs(char *string) {
  int i = 0;
  int args = 0;

  for(i = 0; string[i] != '\0'; ++i) {
    if(args == 0) args = 1;
    else if(string[i] == ' ') ++args;
  }
  return args;
}

int checkargs(int numargs, int type, char *string, struct arm *a) {
  struct module *m;
  void (*cmd)();
    /* Type 0: Exact 1: More 2: Less */

  if((type == 0 && countargs(string) != numargs) || (type == 1 && countargs(string) < numargs) || (type == 2 && countargs(string) > numargs)) {
    say(a->parsevars->sender, "Wrong number of arguments.", a);
    m = findmodule("MSGHandlers");
    if(m != NULL) {
      cmd = dlsym(m->fl, "m_help");
      cmd(a->parsevars->command, a);
    }
    return 0;
  } else return 1;
}


void add (struct activeuser *u) {
  /* Make a whois and add the user */ 

  char whois[10+NICKLEN] = "";

#ifdef Q
  if(strcasecmp(u->nick, Q_NICK) == 0) return;
#ifdef L
  if(strcasecmp(u->nick, L_NICK) == 0) return;
#endif
#endif

  sprintf(whois, "WHOIS %s\n\r", u->nick);
  putserver (whois, bestarm());
  return;
}

void violation (char *sender, char action[256], char *channel) {
  char msg[512];
  char tmstr[100];
  time_t tim;
  struct tm *tmstruct;

  tim = time(NULL);
  tmstruct = localtime(&tim);
  strftime(tmstr, 9, "%H:%M:%S", tmstruct);

  sprintf(msg, "%c4Violation:%c %c%s%c %s %c%s%c (%c%s%c)", COLORS, COLORS, BOLD, sender, BOLD, action, BOLD, channel, BOLD, BOLD, tmstr, BOLD);
  privmsg(HOMECHAN, msg);
}

void cquit(int unused) {
  quit("Killed by console");
}

void sigusr1(int unused) {
  pthread_mutex_unlock(&mysqlmutex); // UnLock mutex
  pthread_mutex_unlock(&globalmutex); // UnLock mutex
  
  printf("Recieved SIGUSR1, unlocking MySQL mutex and globalmutex.\n");
}

void seg(int unused) {
#ifdef DEBUG
  system("xmms --stop && echo \"Natasha sefmentation fault\" | festival --tts &");
#else
  system("sleep 10 && ./natasha &");
#endif
  printf("Segmentation fault\n");
  exit(1);
}

void quit (char reason[]) {
  struct arm *a;
  char msg[512];

#ifdef MAIN
#ifdef SAFEHOME
  snprintf(msg, 512, "MODE " HOMECHAN " -k %s\n\r", homekey); /* Remove key */
  puttoserver(msg, firstarm);
#endif
#endif

  sprintf(msg, "QUIT :%s\n\r", reason);
  
  a = firstarm;
  while (a != NULL)
    {
      if(a->status == 1) {
	puttoserver(msg, a);
	close(a->fd);
	usleep(100);
      }
      a = a->next;
    }

  flushlog();
  printf("Quiting: %s\n", reason);
  mysql_close(sock);
  mysql_close(logsock);
  exit(0);
}

/* Utility functions that actually do read/writes from the server. */

void puttoserver (char *text, struct arm *a)
{
  int l, j;
 
  l = strlen (text);
 
  a->datasent += l;

  /* some sleep time */
  if(a->datasent > 300) (a == firstarm) ? (a->nextsend = time(NULL) + a->datasent/100 + 2) : (a->nextsend = time(NULL) + a->datasent/100);
  
  j = write (a->fd, text, l);
#ifdef DEBUG
  write (1, text, l);
#endif

  if (j != l)
    {
      perror ("write:");
      linkbreak(a);
    }
}

char *getline (struct arm *a) {
  static char buffer[513]; /* one more than defined maximum length */
  int i = 0, j;

  i = 1;
  buffer[i] = 0;
  while ((buffer[i - 1] != '\n') && (i < 512))
    {
      j = read (a->fd, &(buffer[i]), 1);
      if (j <= 0)
	{
	  perror ("read");
	  linkbreak(a);

	  buffer[i]=0;
	  return &(buffer[i]);
	}
      if (j == 0)
	{
	  linkbreak(a);

	  buffer[i] = 0;
	  return &(buffer[i]);
	}
      i++;
    }
  buffer[i] = 0;
  // printf("buffer(%i): %s\n", aid, &(buffer[1]));
  return &(buffer[1]);
}

void linkbreak (struct arm *a) {
  char query[512];
  struct activechanuser *acu;
  
  printf("Link break for: %s\n", a->nick);
  
  if(a->fd) close(a->fd);
  a->fd = 0;

  a->status = 0;
  a->nextsend = time(NULL) + RECONFREQ;
  
  sprintf(query, "UPDATE `arms` SET `status` = '0' WHERE `id` = '%i'", a->id);
  dbquery(query);
  EndDbQuery();
  
  if(a->channels != 0) {
    acu = firstacu;
    while (acu != NULL)
      {
	if(acu->channel->arm == a) {
	  acu->channel->ison = 0;
	  delachanuser(acu->user->nick, acu->channel->name);
	}
	acu = acu->next;
      }
    
    a->channels = 0;
  }
}

void startlink (struct arm *a)
{
  struct hostent *server = NULL;
  struct sockaddr_in server_addr;
  char line[512];
  char send[512];
#ifdef OUTGOING_VHOST
  struct hostent *vhost = NULL;
  struct sockaddr_in local_addr;
#endif

  a->status = 1;

  while (a->firstsendqueue != NULL) delsendqueue(a);

  if (a->fd)
    {
      close (a->fd);
    }

  printf("Connecting arm: %s\n", a->nick);

  server = gethostbyname(SERVER);
  if(server == NULL) {
    printf("Looking up server hostname failed.\n");
    a->status = 0;
    return;
  }

  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(IRCPORT);
  memcpy (&(server_addr.sin_addr.s_addr), server->h_addr, server->h_length);

  a->fd = socket (AF_INET, SOCK_STREAM, 0);
  
#ifdef OUTGOING_VHOST
  vhost = gethostbyname(a->vhost);
  if(vhost == NULL) {
    printf("Looking up vhost failed.\n");
    a->status = 0;
    return;
  }

  memset(&local_addr, 0, sizeof(struct sockaddr_in));
  local_addr.sin_family = AF_INET;
  memcpy (&(local_addr.sin_addr.s_addr), vhost->h_addr, vhost->h_length);

  if(bind(a->fd, (struct sockaddr *) &local_addr, sizeof(struct sockaddr_in))) {
    perror ("bind");
    a->status = 0;
    return;
  }
#endif

  if (connect (a->fd, (struct sockaddr *) &server_addr, sizeof (server_addr)))
    {
      perror ("connect");
      a->status = 0;
      return;
    }

  a->nextsend = 0;
  
  sprintf(send, "UPDATE `arms` SET `status` = '1' WHERE `id` = '%i'", a->id);
  dbquery(send);
  EndDbQuery();

  /* Establish a client connection. */
  sprintf(send, "USER %s +iw %s :Natasha service owned by: %s\n\r", USERNAME, a->nick, BOTHOUSE);
  puttoserver(send, a);

  sprintf(send, "Nick %s\n\r", a->nick);
  puttoserver(send, a);

  while (strstr(line, " 255 ") == NULL) {
    strcpy(line, getline(a));

    if(a->status == 0) return; // Return if connection goes down

    if(strstr(line, " 433 ") != NULL) {

      printf("Warning: Nick %s is stolen.\n", a->nick);

      sprintf(send, "%c4Warning:%c Nick %c%s%c is stolen.", COLORS, COLORS, BOLD, a->nick, BOLD);
      privmsg(HOMECHAN, send);
      
      puttoserver("QUIT :Nick stolen\n\r", a);
      usleep(200);

      linkbreak(a);
      return;
    }

    if (strstr(line, " 001 ") != NULL) {
      sscanf(line, ":%s 001 ", a->server);
#ifdef DEBUG
      printf("Server name set to: %s\n", a->server);
#endif
    }
    if (strstr(line, "PING :") != NULL) {
#ifdef DEBUG
      printf("Ping<>Pong\n");
#endif
      serverpong(strstr(line, "PING"), a);
    }
#ifdef DEBUG
#ifdef SKIPMOTD
    if(strstr(line, " 372 ") == NULL && strstr(line, " 375 ") == NULL && strstr(line, " 376 ") == NULL) 
#endif
      printf("Server: %s\n", line);
#endif
  }
#ifdef DEBUG
  printf("Connected.\n");
#endif
 
#ifdef Q
  /* Q auth stuff */
  
  sprintf(line, "%s@%s", Q_NICK, Q_HOST);
  sprintf(send, "AUTH %s %s", a->nick, Q_PASS);
  exprivmsg(line, send, a);
#ifdef DEBUG
  printf("Q AUTH'd.\n");
#endif
#endif
  
  rejoinchannel(HOMECHAN, a);
}

void parse (char *buffer, struct arm *a)
{
  int i, j;
  char msg[512] = "";
  char host[HOSTLEN] = "";
  char username[USERLEN] = ""; 
  struct activeuser *u;

#ifdef DEBUG
  //  printf("Buffer: %s", buffer);
#endif

  for (i = 0; i < MAX_ARGS; i++) strncpy(a->parsevars->args[i], "", 512);

  a->parsevars->argcount = 0;
  a->parsevars->sender[0] = 0;
  a->parsevars->command[0] = 0;
  
  /* Skip over any leading spaces. */
  for (i = 0; buffer[i] && (buffer[i] == ' '); i++);
  /*
   * "i" had better increase monatomically from here on, or we're in
   * a lot of trouble. :)
   */
  /* Check to see who sent it. */

  if (buffer[i] == ':')
    {
      j = 0;
      for (i++; buffer[i] && (buffer[i] != '!' && buffer[i] != ' '); i++)
	{
	  a->parsevars->sender[j] = buffer[i];
	  j++;
	}
      
      a->parsevars->sender[j] = 0;
         
      if(buffer[i] == '!') {
	++i;
	for(j = 0; buffer[i] != '@'; ++i) {
	  username[j] = buffer[i];
	  ++j;
	}

	++i;
	for(j = 0; buffer[i] != ' '; ++i) {
	  host[j] = buffer[i];
	  ++j;
	}
	
	j = 0;

	a->parsevars->isserver = 0;
      } else a->parsevars->isserver = 1;
    } 

   if (!buffer[i])
    {
      return;
    }

  /* Skip over leading spaces */
  while (buffer[i] && (buffer[i] == ' ')) i++;
  j = 0;
  while ((buffer[i + j]) && !isspace (buffer[i + j]) && j < 512) j++;
  memcpy(a->parsevars->command, buffer + i, j);
  a->parsevars->command[j] = 0;

  /* 
   * Good news, we got the command and the sender (probably).
   * Now all we need is the parameters and we're in business. :)
   */

  i = i + j;
  while ((buffer[i]) && (buffer[i] != '\n') && (buffer[i] != '\r'))
    {
      while (buffer[i] == ' ')
	i++;			/* Again, do a standard spaces-skip job. */
      j = 0;
      if (buffer[i] == ':')
	{
	  i++;			/* Skip over the : */
	  while ((buffer[i] != 0) && (buffer[i] != '\n') && (buffer[i] != '\r'))
	    {
	      a->parsevars->args[a->parsevars->argcount][j] = buffer[i];
	      i++;		/* Next char in both the source */
	      j++;		/* and the destination strings  */
	    }
	  a->parsevars->args[a->parsevars->argcount][j] = 0;
	}
      else
	{
	  while ((buffer[i] != 0) && (buffer[i] != '\n') && (buffer[i] != '\r') && (buffer[i] != ' '))
	    {
	      a->parsevars->args[a->parsevars->argcount][j] = buffer[i];
	      i++;
	      j++;
	    }
	  a->parsevars->args[a->parsevars->argcount][j] = 0;
	}
      a->parsevars->argcount++;
    }

  for (j = 0; servtab[j].func != NULL; j++)
    {
      if (!strcmp (a->parsevars->command, servtab[j].msg))
	{
	  u = findauser(a->parsevars->sender);

	  if(u == NULL && (strcmp(a->parsevars->command, "NICK") == 0 || strcmp(a->parsevars->command, "QUIT") == 0)) return;

	    if(u == NULL && a->parsevars->isserver == 0 && strcmp("", a->parsevars->sender) != 0) {
#ifdef DEBUG
	      printf("New active user: %s host: %s username: %s command: %s\n", a->parsevars->sender, host, username, a->parsevars->command);
#endif
	      u = addauser(a->parsevars->sender, username, host);
#ifdef DEBUG
	      printf("- Doing whois\n");
#endif
#ifdef Q_AUTOAUTH
	      add(u);
#endif
	    }
	    if(u != NULL) u->lastseen = time(NULL);

	    i = 1;
	    while(i < MAX_ARGS && a->parsevars->args[i][0] != '\x0') {
	      if(strcmp(a->parsevars->command, "PRIVMSG") != 0 && i != 1) strcat(msg, " ");
	      strcat(msg, a->parsevars->args[i]);
	      ++i;
	    }
	    if(u != NULL || a->parsevars->isserver == 1 || strcmp(a->parsevars->command, "PING") == 0) {
      
	      /* First we log */
#ifdef DEBUG
	      printf("- Logging\n");
#endif
	      addlog(a->parsevars->sender, a->parsevars->args[0], a->parsevars->command, msg);
	   
#ifdef DEBUG
	      printf("- Exec: %s\n", a->parsevars->command);
#endif
	      amodfunc("IRCHandlers", servtab[j].func, a);
#ifdef DEBUG	      
	      printf("- End\n");
#endif
	    }
	    return;
	}
    }
}

/* A main function to handle the reads/sends for each arm */

void *armthread (void* ptr) {
  struct arm *a = (struct arm*)ptr;
  struct timeval tm;
  int rc;

  startlink(a);
  if(a->status == 0) return NULL;
  joinchannels(a);

  while(a->status == 1) {
    /* First we send data in queue */
    
    if(time(NULL) >= a->lastreset+EXCESSRESET) {
      a->datasent = 0;
      a->nextsend = 0;
      a->lastreset = time(NULL);
    }
     
    if((a->nextsend < time(NULL)) && (a->firstsendqueue->data != NULL)) {
      puttoserver(a->firstsendqueue->data, a);
      delsendqueue(a);
    }
    
   
    /* Now we read */
    
    tm.tv_usec=50000;
    tm.tv_sec=0;
    FD_ZERO (&a->r);
    FD_SET (a->fd, &a->r);
    
    rc = select (a->fd + 1, &a->r, NULL, NULL, &tm);
    if (rc==-1) {
      if (errno!=EINTR)
        {
          perror("select!=EINTR");
        } else {
          perror("select==EINTR");
        }
    } else if (rc>0) {
      if (FD_ISSET(a->fd,&a->r)) {
	if(!pthread_mutex_trylock(&globalmutex)) {
	  parse(getline(a), a);
	  pthread_mutex_unlock(&globalmutex);
	} else usleep(500000);
      } else {
        fprintf(stderr,"FD_ISSET unknown.\n");
      }
    }
  }
  return NULL;
}

int main (int argc, char **argv)
{
  char tmp[512];
  int lastmysql, i;
  struct arm *a;

  signal(SIGINT, cquit);
  signal(SIGSEGV, seg);
  signal(SIGUSR1, sigusr1);
  signal(SIGUSR2, stresstest);
  
  printf("%s\n\nConnecting to MySQL database.\n", CTCP_VERSION_REPLY);
  mysql_init(&mysql);
  mysql_init(&logmysql);
  dbconnect();
  logdbconnect();

  printf("Loading database.\n");
  loadarms();
  loadchannellist();
  
  printf("Loading modules.\n");
  loadmodulesinconf(); 
#ifdef SAFEHOME
  strncpy(homekey, loaddatafromfile("homekey"), KEYLEN);
#endif

  i = time(NULL);
  a = firstarm;
  while (a != NULL)
    {
      a->nextsend = i;
      i += RECONFREQ;
      a = a->next;
    }

  nextcheck = time(NULL);
  lastmysql = time(NULL);

  rejoint = time(NULL) + 180;
#ifdef MAIN
#ifdef SAFEHOME
  nextsecure = time(NULL) + 60;
#endif
#endif
  printf("Initiating main loop.\n\n");

  /* Main loop */
  while (1)
    {          
      /* Here we check for some "timeouts" */
      if(time(NULL) >= nextcheck) {
#ifdef DEBUG
	printf("Processing scheduled tasks.\n");
#endif

	/* Reload MSGHandlers or IRCHandlers if requested */
	if(reloadmsgmod == 1) {
	  strncpy(tmp, findmodule("MSGHandlers")->path, 100);
	  unloadmodule("MSGHandlers");
	  loadmodule(tmp, 0);
	  
	  reloadmsgmod = 0;
	}
	if(reloadircmod == 1) {
	  strncpy(tmp, findmodule("IRCHandlers")->path, 100);
	  unloadmodule("IRCHandlers");
	  loadmodule(tmp, 1);
	  
	  reloadircmod = 0;
	}

	modfunc("Graph", "graphmain"); // The graphs
        modfunc("Request", "spewchan"); // Spew request data to public channe

#ifdef MAIN
#ifdef SAFEHOME
	/* Home channel security */
	if(nextsecure < time(NULL)) {
	  securehome();
	  nextsecure = time(NULL) + SECURETIME;
	}
#endif
#endif
	/* MySQL keepalive */

	if(lastmysql+10000 < time(NULL)) {
#ifdef DEBUG
	  printf("Reconnect MySQL\n");
#endif
	  pthread_mutex_lock(&mysqlmutex); // Lock mutex
	  mysql_close(sock);
	  dbconnect();
	  pthread_mutex_unlock(&mysqlmutex); // UnLock mutex


	  pthread_mutex_lock(&logmysqlmutex); // Lock mutex
	  mysql_close(logsock);
	  logdbconnect();
	  pthread_mutex_unlock(&logmysqlmutex); // UnLock mutex

	  lastmysql = time(NULL);
	}

	// Join dead threads
	pthread_mutex_lock(&globalmutex); // Lock MuteX

	a = firstarm;
	while (a != NULL)
	  {
	    if(a->status == 0 && a->activethread == 1) {
	      if(pthread_join(a->thread, NULL)) printf("Error joining thread for %s.", a->nick);
	      a->activethread = 0;
	    }
	    a = a->next;
	  }

	pthread_mutex_unlock(&globalmutex); // UnLock MuteX

	// Create threads for disconnected arms
	
	if(connectarms == 1) {
	  pthread_mutex_lock(&globalmutex); // Lock MuteX
	
	  a = firstarm;
	  while (a != NULL)
	    {
	      if(a->status == 0 && a->activethread == 0 && a->nextsend <= time(NULL) && a->connect == 1) {
		if(pthread_create(&a->thread, NULL, armthread, a)) printf("Error creating thread for %s.", a->nick);
		a->activethread = 1;	
		a = NULL;
	      } else a = a->next;
	    }
	  pthread_mutex_unlock(&globalmutex); // UnLock MuteX
	}

#ifdef DEBUG
	printf("Timer User Loop.\n");
#endif
	// Clear old activeusers and bans
	TimerUserLoop();
#ifdef DEBUG
	printf("Timer Channel Loop\n");
#endif
	TimerChannelLoop();
	
	CleanUpOldBadChans();
#ifdef DEBUG
	printf("End Of Loops\n");
#endif
	// Set new nextcheck
	nextcheck = time(NULL) + CHECK_TIME;
      }
      usleep(500000);
    }
  return 0;
}


/* Stresstest code */

void stresstest(int unused) {
  struct arm *a;
  a = findarm("Natasha-dev");

  pthread_mutex_lock(&globalmutex); // Lock MuteX

  printf("Stresstest begin\n");
  while(1) parse(":barrysworld1.uk.quakenet.org 330 Natasha-dev BteEst SkalMan :is authed as\n\r", a);
}
