/* * bot.c * (C) Peter Salanki 2004 * This program is copyright, and covered by the Gnu Public License. * The Natasha bot. * sorcer@linux.se */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globals.h" #include "modules/irchandlers/servtab.h" #ifdef SERVERLIST struct ServerListEntry { char *host; }; struct ServerListEntry serverlist[] = { {"ludd.se.quakenet.org"}, {"stockholm.se.quakenet.org"}, {"mediatraffic.fi.quakenet.org"}, {"chat.uk.quakenet.org"}, {"irc.it.quakenet.org"}, {"tiscali.dk.quakenet.org"}, {"blueyonder.uk.quakenet.org"}, {"snoke.nl.quakenet.org"}, {"demon.uk.quakenet.org"}, {"b0rk.uk.quakenet.org"}, {"ign.ie.quakenet.org"}, {"jubiigames.dk.quakenet.org"}, {"fragland.be.quakenet.org"}, {"barrysworld.uk.quakenet.org"}, {"splatterworld.de.quakenet.org"}, {"euroserv.fr.quakenet.org"}, {"wineasy.se.quakenet.org"}, {"whole.mi.us.quakenet.org"}, {"whole.ny.us.quakenet.org"}, {"oslo.no.quakenet.org"}, {"DONE"} }; int curserver; #endif int nextcheck; int lines = 0; int rejoint = 0; int reloadmsgmod = 0; int reloadircmod = 0; int hourly = 0; int armcount = 0; short connectarms = 1; #ifdef SAFEHOME #ifdef MAIN int nextsecure; #endif char homekey[KEYLEN]; #endif char compiledate[] = __DATE__ " " __TIME__; int clicks = 0; int clicktime = 0; int htm = 0; int autohtmlimit = 9999999; #ifdef LOGALL FILE *fLogAll = NULL; #endif char *LogQueryBuffer = NULL; pid_t mainpid; MYSQL mysql, *sock; MYSQL_RES *res; MYSQL_ROW row; MYSQL logmysql, *logsock; MYSQL_RES *logres; MYSQL_ROW logrow; #ifdef Q struct activeuser *Qu = NULL; #ifdef L struct activeuser *Lu = NULL; #endif #endif struct module *irchandlersmodule = NULL; /* Global mutexes */ pthread_mutex_t mysqlmutex=PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t logmysqlmutex=PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t MainMuteX=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 leakcheck(int); #ifdef STRESSTEST void stresstest(int); #endif void memleak_fn(const char* file, int line, size_t size, void* ptr); /* * 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,MYSQLDB_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)+1); 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(1); } 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; } int appenddatatofile (char *file, char *data) { FILE *pFile; pFile = fopen(file, "a"); if(pFile == NULL) { printf("Could not open: %s.\n", file); return 0; } fputs(data, pFile); fclose(pFile); return 1; } #ifdef LOGALL void OpenLogAll(void) { if((fLogAll = fopen(LOGALL, "a")) == NULL) { printf("Could not open LogAll file: " LOGALL ".\n"); exit(1); } } #endif #ifdef MAIN #ifdef SAFEHOME void securehome (void) { char msg[512]; char newkey[KEYLEN], realrandstr[KEYLEN-4]; char *randstr = NULL; srand (time(NULL)); /* Init random number generator */ snprintf(msg, 512, "MODE " HOMECHAN " -k %s\n\r", homekey); /* Remove old key */ putserver(msg, firstarm); randstr = RandString(KEYLEN-4); strncpy(realrandstr, randstr, KEYLEN-4); snprintf(newkey, KEYLEN, "N%s%i", realrandstr, rand()); /* Generate new key */ Free(randstr); 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, a->parsevars->u); 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(u == Qu) return; #ifdef L else if(u == Lu) return; #endif #endif #ifdef UMODE_x if(strstr(u->hostname, UMODE_x_hostappend) != NULL) { STR_replace_c(u->hostname, UMODE_x_hostappend, "", whois); AuthUser(u, whois); } else { #endif sprintf(whois, "WHOIS %s\n\r", u->nick); putserver (whois, bestarm()); #ifdef UMODE_x } #endif 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) { if(getpid() == mainpid) quit("Killed by console"); } void sigusr1(int unused) { if(getpid() != mainpid) return; pthread_mutex_unlock(&mysqlmutex); // UnLock mutex Unlock(); // UnLock mutex printf("Recieved SIGUSR1, unlocking MySQL mutex and MainMuteX.\n"); } void seg(int unused) { if(getpid() == mainpid) { #ifdef DEBUG // system("xmms --stop && echo \"Natasha segmentation 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 Unlock(); Lock(); sprintf(msg, "QUIT :%s\n\r", reason); a = firstarm; while (a != NULL) { if(a->status == 1 && (a->fd)) { puttoserver(msg, a); close(a->fd); a->status = 0; usleep(100); } a = a->next; } flushlog(); printf("Quiting: %s\n", reason); mysql_close(sock); mysql_close(logsock); exit(0); } void clickput(void) { char msg[512]; if(time(NULL) >= clicktime) { snprintf(msg, 512, "Click count: %c%i%c", BOLD, clicks, BOLD); privmsg(HOMECHAN, msg); if(clicks >= autohtmlimit && htm == 0) { htm = 1; snprintf(msg, 512, "HTM is now enabled (auto), click count: %c%i%c", BOLD, clicks, BOLD); privmsg(HOMECHAN, msg); } else if(clicks < autohtmlimit && htm == 1) { htm = 0; snprintf(msg, 512, "HTM is now disabled (auto), click count: %c%i%c", BOLD, clicks, BOLD); privmsg(HOMECHAN, msg); } clicktime = time(NULL)+300; clicks = 0; } } /* Utility functions that actually do read/writes from the server. */ void puttoserver (char *text, struct arm *a) { int l, j; l = strlen (text); if(!(a->fd)) return; a->datasent += l; /* some sleep time */ if(a->datasent > 300) a->nextsend = time(NULL) + a->datasent/100; #ifdef BIGLOAD else if(a == firstarm && a->datasent > 35) a->nextsend = time(NULL) + 2; #endif j = write (a->fd, text, l); #ifdef DEBUG write (1, text, l); #endif if (j != l) { if(errno == EAGAIN) return; 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) { if(errno == EAGAIN) { buffer[i]=0; return &(buffer[1]); } 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(%s): %s", a->nick, &(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 = -1; 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, acu->user); } 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); #ifdef SERVERLIST server = gethostbyname(serverlist[curserver].host); if(server == NULL) { printf("Looking up server: %s hostname failed.\n", serverlist[curserver].host); snprintf(line, 512, "Looking up server: %s hostname failed.", serverlist[curserver].host); privmsg(HOMECHAN, line); if(strcmp("DONE", serverlist[curserver+1].host) == 0) curserver = 0; else ++curserver; a->status = 0; return; } snprintf(line, 512, "Connecting: %s to server: %s", a->nick, serverlist[curserver].host); privmsg(HOMECHAN, line); if(serverlist[curserver+1].host == NULL) curserver = 0; else ++curserver; #else server = gethostbyname(SERVER); if(server == NULL) { printf("Looking up server hostname failed.\n"); a->status = 0; return; } #endif 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"); close(a->fd); 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"); close(a->fd); a->status = 0; return; } #endif if (connect (a->fd, (struct sockaddr *) &server_addr, sizeof (server_addr))) { perror ("connect"); close(a->fd); 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. */ #ifdef NBNC sprintf(send, "PASS %s:any\n\r", a->nick); puttoserver(send, a); #endif sprintf(send, "USER %s -iws %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) { close(a->fd); return; // Return if connection goes down } #ifndef NBNC if(strstr(line, " 433 ") != NULL) { #else if(strstr(line, " p433 ") != NULL) { #endif #if 0 } /* emacs fix */ #endif 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); a->nextsend = time(NULL) + RECONFREQ*30; 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 fcntl(a->fd, F_SETFL, O_NONBLOCK); /* Put socket in non-blocking mode */ #ifdef Q /* Q auth stuff */ q_auth(a); #endif /* For auto +x enable this */ #if 0 sprintf(send, "MODE %s +x\n\r", a->nick); puttoserver(send, a); #endif rejoinchannel(HOMECHAN, a); joinchannels(a); } void parse (char *buffer, struct arm *a) { int i, j; char msg[512] = ""; char host[HOSTLEN] = ""; char username[USERLEN] = ""; struct activeuser *u; void (*cmd)(struct arm *arm); #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 #ifdef UMODE_x if(u->auth == UNAUTHED) #endif 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) { a->parsevars->u = u; /* Optomized pointing */ /* First we log */ #ifdef DEBUG printf("- Logging\n"); #endif addlog(a->parsevars->sender, a->parsevars->args[0], a->parsevars->command, msg, a); #ifdef DEBUG printf("- Exec: %s\n", a->parsevars->command); #endif if(irchandlersmodule != NULL) { cmd = dlsym(irchandlersmodule->fl, servtab[j].func); cmd(a); } #ifdef DEBUG ++clicks; printf("- End\n"); #endif } return; } } } int main (int argc, char **argv) { char tmp[512]; int lastmysql, i; struct arm *a; fd_set readfds; int activity; #ifdef NBNC int connectcount = 0; #endif struct timeval tm; time_t ctime; signal(SIGINT, cquit); signal(SIGSEGV, seg); signal(SIGUSR1, sigusr1); signal(SIGPIPE, SIG_IGN); #ifdef STRESSTEST signal(SIGUSR2, stresstest); #elif defined(DEBUG) signal(SIGUSR2, leakcheck); #endif mainpid = getpid(); 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(); irchandlersmodule = findmodule("IRCHandlers"); #ifdef SAFEHOME strncpy(homekey, loaddatafromfile("homekey"), KEYLEN); #endif autohtmlimit = atoi(loaddatafromfile("autohtm")); LogQueryInit(); /* Allocate and begin the Log Query Buffer */ #ifdef LOGALL OpenLogAll(); /* Open the LogAll file */ #endif /* i = time(NULL); activity = 0; a = firstarm; while (a != NULL) { a->nextsend = i; ++activity; if(activity == 3) { i += 60; // Add 60 seconds activity = 0; } a = a->next; } */ i = time(NULL); a = firstarm; while (a != NULL) { a->nextsend = i; #ifdef NBNC i += 1; /* Add only one second */ #else i += RECONFREQ; /* Add 60 seconds */ #endif a = a->next; } nextcheck = time(NULL); lastmysql = time(NULL); hourly = time(NULL) + 3600; rejoint = time(NULL) + 180; #ifdef MAIN #ifdef SAFEHOME nextsecure = time(NULL) + 60; #endif #endif #ifdef SERVERLIST curserver = 0; #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 Lock(); /* Lock Natasha */ /* 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"); irchandlersmodule = loadmodule(tmp, 1); reloadircmod = 0; } modfunc("Graph", "graphmain"); // The graphs modfunc("Request", "spewchan"); // Spew request data to public channel modfunc("puffNotes", "remind"); // Remind people of their notes #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); } // Connect arms if(connectarms == 1) { a = firstarm; while (a != NULL) { if(a->type == -1) { if(a->next != NULL) { a = a->next; delarm(a->last->nick); } else { a = a->last; delarm(a->next->nick); } } #ifdef SERVERLIST if(a->status == 0 && a->connect == 1) { #else if(a->status == 0 && a->nextsend <= time(NULL) && a->connect == 1) { #endif startlink(a); #ifndef NBNC a = NULL; #else if(connectcount == 10) { a = NULL; connectcount = 0; } else ++connectcount; #endif } else a = a->next; } } #if 0 } /* We need this for emacs indent to work properly */ #endif #ifdef DEBUG printf("Timer User Loop.\n"); #endif // Clear old activeusers and bans TimerUserLoop(); #ifdef DEBUG printf("Timer Channel Loop\n"); #endif TimerChannelLoop(); CleanUpOldBadChans(); CleanUpOldSuspends(); #ifdef DEBUG printf("End Of Loops\n"); #endif clickput(); // Handle clicks Unlock(); /* Unlock Natasha */ // Set new nextcheck nextcheck = time(NULL) + CHECK_TIME; } /* Handle socket operations */ FD_ZERO(&readfds); for(a = firstarm; a != NULL; a = a->next) if(a->fd > 0) FD_SET(a->fd, &readfds); /* wait for something to heappen, max 1 second */ tm.tv_usec = 50000; tm.tv_sec = 0; activity = select(armcount+300, &readfds, NULL, NULL, &tm); if ((activity < 0) && (errno!=EINTR)) { /* there was an error with select() */ perror("select"); } ctime = time(NULL); /* Temporary time holder, increase performance */ /* Arm connections */ Lock(); /* Lock Natasha */ for(a = firstarm; a != NULL; a = a->next) { if(a->status == 1) { if(FD_ISSET(a->fd, &readfds)) { parse(getline(a), a); #if 0 valread = read(a->fd, buffer, 512); if(valread <= 0) { linkbreak(a); } else { // set the terminating NULL byte on the end of the data read buffer[valread] = 0; parse(buffer, a); /* Parse the incoming data */ } #endif } /* Send data */ if(ctime >= a->lastreset+EXCESSRESET) { a->datasent = 0; a->nextsend = 0; a->lastreset = time(NULL); } if((a->nextsend < ctime) && (a->firstsendqueue->data != NULL)) { puttoserver(a->firstsendqueue->data, a); delsendqueue(a); } } } Unlock(); /* Unlock Natasha */ } return 0; } #ifdef STRESSTEST /* Stresstest code */ void stresstest(int unused) { struct arm *a; a = findarm("Natasha-dev"); Lock(); // Lock MuteX printf("Stresstest begin\n"); while(1) parse(":barrysworld1.uk.quakenet.org 330 Natasha-dev BteEst SkalMan :is authed as\n\r", a); } #endif #ifdef DEBUG /* Memory leak detection functions */ void leakcheck (int unused) { if(getpid() != mainpid) return; printf("Checking for leaks\n"); //fda_clear_refs(); // fda_enum_leaks(memleak_fn); } void memleak_fn(const char* file, int line, size_t size, void* ptr) { if((strcasecmp(file, "src/memory.c") == 0 && (line == 523 || line == 453 || line == 415 || line == 450 || line == 147 || line == 26 || line == 37 || line == 38 || line == 87 || line == 291 || line == 540|| line == 120 || line == 179 || line == 540 || line == 346)) || (strcasecmp(file, "src/database.c") == 0 && (line == 667))) return; Lock(); /* Lock Natasha */ printf("Memory leak: %s: %d - %d bytes (%p)\n", file, line, size, ptr); privmsgf(HOMECHAN, "Memory leak: %s: %d - %d bytes at (%p)\n", file, line, size, ptr); Unlock(); /* UnLock Natasha */ } #endif