/* * database.c * (C) Peter Salanki 2002 * 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 "globals.h" #include "bottypes.h" #include "irc_chattr.h" int userlevel (char *username) { struct activeuser *u; u = findauser(username); if (u != NULL) { return (u->auth); } else { return 0; } } int chanuserlevel (char *username, char *channel) { struct activeuser *u; struct channel *c; struct chanuser *cu; u = findauser(username); c = findchannel(channel); #ifdef Q if(strcasecmp(username, Q_NICK) == 0) return 5; #ifdef L if(strcasecmp(username, L_NICK) == 0) return 5; #endif #endif if (u != NULL && u->auth >= NORMAL) { if (c != NULL) { cu = findchanuser(u->userid, c->id); if(cu != NULL) return (cu->level); else return 0; } else return 0; } else { return 0; } } int chanusergreet (char *username, char *channel, char *ret) { struct activeuser *u; struct channel *c; struct chanuser *cu; u = findauser(username); c = findchannel(channel); if (u != NULL && u->auth >= NORMAL) { if (c != NULL) { cu = findchanuser(u->userid, c->id); if(cu != NULL && strcmp(cu->greet, "") != 0) { strncpy(ret, cu->greet, 512); return 1; } else return 0; } else return 0; } else { return 0; } } char *authtotext(int auth) { char *text = malloc(18); if(auth == UNAUTHED) strcpy(text, "Not logged in"); else if(auth == NORMAL) strcpy(text, "Normal user"); else if(auth == SERVICE_FRIEND) strcpy(text, "Service friend"); else if(auth == HELPER) strcpy(text, "Helper"); else if(auth == QUEUE_WORKER) strcpy(text, "Queue worker"); else if(auth == TECHNICIAN) strcpy(text, "Technician"); else if(auth == MASTER_TECHNICIAN) strcpy(text, "Master Technician"); else if(auth == IRC_OPERATOR) strcpy(text, "IRC Operator"); else if(auth == BOT_NORMAL) strcpy(text, "Bot"); else if(auth == BOT_PRIVATE) strcpy(text, "Private Bot"); else if(auth == BOT_CHECK) strcpy(text, "Checker Service"); else strcpy(text, "Undefined"); return text; } void chanlevtotext(int level, char *text) { if(level == CHAN_NOACCESS) strcpy(text, "No access"); else if(level == CHAN_FRIEND) strcpy(text, "Friend"); else if(level == CHAN_VOICE) strcpy(text, "Voice"); else if(level == CHAN_OP) strcpy(text, "Operator"); else if(level == CHAN_MASTER) strcpy(text, "Master"); else if(level == CHAN_OWNER) strcpy(text, "Owner"); else strcpy(text, "Undefined"); } int texttochanlev(char *text) { if(strcasecmp(text, "No") == 0) return CHAN_NOACCESS; else if(strcasecmp(text, "Friend") == 0) return CHAN_FRIEND; else if(strcasecmp(text, "Voice") == 0) return CHAN_VOICE; else if(strcasecmp(text, "Op") == 0) return CHAN_OP; else if(strcasecmp(text, "Master") == 0) return CHAN_MASTER; else if(strcasecmp(text, "Owner") == 0) return CHAN_OWNER; else return 99; // Level doesen't exist } int modfunc(char *module, char *function) { char msg[512]; struct module *m; void (*cmd)(); const char *err; m = findmodule(module); if(m != NULL) { cmd = dlsym(m->fl, function); err = dlerror(); if(err) { snprintf(msg, 512, "%c4Warning:%c Call for function failed (%c%s%c in module %c%s%c). Error: %s", COLORS, COLORS, BOLD, function, BOLD, BOLD, module, BOLD, err); privmsg(HOMECHAN, msg); return 0; } cmd(); return 1; } return 0; } int amodfunc(char *module, char *function, struct arm *arm) { char msg[512]; struct module *m; void (*cmd)(struct arm *arm); const char *err; m = findmodule(module); if(m != NULL) { cmd = dlsym(m->fl, function); err = dlerror(); if(err) { snprintf(msg, 512, "%c4Warning:%c Call for function failed (%c%s%c in module %c%s%c). Error: %s", COLORS, COLORS, BOLD, function, BOLD, BOLD, module, BOLD, err); privmsg(HOMECHAN, msg); return 0; } cmd(arm); return 1; } return 0; } /* * ircd_strcmp - case insensitive comparison of 2 strings * NOTE: see ircd_chattr.h for notes on case mapping. * NOTE: This function is a direct ripoff from the the Undernet IRCd. */ int irc_strcmp(const char *a, const char *b) { const char* ra = a; const char* rb = b; while (ToLower(*ra) == ToLower(*rb)) { if (!*ra++) return 0; else ++rb; } return (*ra - *rb); } void TimerUserLoop(void) { /* This loop handles all timer based actions to the user structure */ struct activeuser *u; struct activeuser *next; pthread_mutex_lock(&globalmutex); // Lock MuteX u = firstauser; while (u != NULL) { if((next = DelOld(u)) == NULL) { #ifdef Q_AUTOAUTH ReCheckAuth(u); #endif u = u->next; } else u = next; } pthread_mutex_unlock(&globalmutex); // UnLock MuteX } #ifdef Q_AUTOAUTH int ReCheckAuth(struct activeuser *u) { /* Check if an unauthed user needs to be auth-checked again */ if(u->auth == UNAUTHED && u->check != 0 && time(NULL) >= u->check) { add(u); u->check = 0; return 1; } else return 0; } #endif struct activeuser *DelOld(struct activeuser *u) { /* This function checks if a user is on any channels, and his idle time. If he isn't seen on any channels, and has exceeded the maximum idle time, he will be deleted from memory */ struct activeuser *NextUser = NULL; if (u->lastseen+UEXPIRE <= time(NULL) && u->auth < BOT_NORMAL && u->firstlink == NULL && u->isinhome == 0) { #ifdef DEBUG printf("User: %s not seen on any channel, deleted. (expire)\n", u->nick); #endif NextUser = u->next; deluser(u); return NextUser; } else return NULL; } void TimerChannelLoop(void) { /* This loop handles all timer based actions to the user structure */ struct channel *c; int rejoin; pthread_mutex_lock(&globalmutex); // Lock MuteX if(time(NULL) >= rejoint) { rejoin = 1; rejoint = time(NULL) + REJOINFREQ; } else rejoin = 0; c = firstchan; while (c != NULL) { DelOldChannelBans(c); /* Delete old channel bans */ CloseEndedVote(c); /* Close votes that have ended */ if(rejoin) ReJoinChannelCheck(c); /* Try to rejoin channels if rejoint(time variable) is exceeded */ c = c->next; } pthread_mutex_unlock(&globalmutex); // Unlock MuteX } void ReJoinChannelCheck(struct channel *c) { /* Check if the bot is not on a channel and rejoin if TRUE. */ if(c->ison == 0 && c->arm->status == 1) { #ifdef DEBUG printf("Rejoining: %s\n", c->name); #endif rejoinchannel(c->name, c->arm); } } void DelOldChannelBans(struct channel *c) { /* This function will remove bans from channels that have expired */ struct banlist *ban; ban = c->firstbanlist; while (ban != NULL) { if(ban->expire <= time(NULL)) delban(ban->host, c->name); ban = ban->next; } } void CloseEndedVote(struct channel *c) { /* This function will close a vote for a channel if it has expired */ char tmp[512]; if(c->votestruct != NULL && c->votestruct->expiretime <= time(NULL)) { if(c->votestruct->yes > c->votestruct->no) snprintf(tmp, 512, "Vote has ended. %cYes%c won with %c%i%c votes against %c%i%c.", BOLD, BOLD, BOLD, c->votestruct->yes, BOLD, BOLD, c->votestruct->no, BOLD); else if(c->votestruct->no > c->votestruct->yes) snprintf(tmp, 512, "Vote has ended. %cNo%c won with %c%i%c votes against %c%i%c.", BOLD, BOLD, BOLD, c->votestruct->no, BOLD, BOLD, c->votestruct->yes, BOLD); else if(c->votestruct->no == c->votestruct->yes) snprintf(tmp, 512, "Vote has ended. Vote ended in tie with %c%i%c yes and no votes.", BOLD, c->votestruct->yes, BOLD); privmsg(c->name, tmp); FreeVote(c); } } struct arm *bestarm(void) { struct arm *a; int leastdatainbuf = 999999999; struct arm *bestarm = NULL; a = firstarm; while (a != NULL) { if((a->datainbuff <= leastdatainbuf) && a->status == 1) { bestarm = a; leastdatainbuf = a->datainbuff; if(leastdatainbuf == 0) return a; } a = a->next; } return bestarm; } void delallachanusers (struct channel *c) { struct activechanuser *acu; /* Delete all active channel users */ acu = firstacu; while (acu != NULL) { if(acu->channel == c) delachanuser(acu->user->nick, c->name); acu = acu->next; } #ifdef DEBUG printf("All active channel users for channel: %s deleted\n", c->name); #endif } int delallauserchans (struct activeuser *u) { struct aculink *aculink; struct aculink *next; #ifdef DEBUG printf("Deleting all active channels for user: %s\n", u->nick); #endif aculink = u->firstlink; while (aculink != NULL) { next = aculink->next; if(delachanuser(u->nick, aculink->link->channel->name)) return 1; aculink = next; } return 0; } void loadarms () { dbquery("UPDATE `arms` SET `status` = 0, `channels` = 1 WHERE `sid` = " SID ""); EndDbQuery(); dbquery("SELECT `id`, `nick`, `type` FROM `arms` WHERE `sid` = " SID " ORDER BY `id`"); while((row = mysql_fetch_row(res))) { addarm (row[1], atoi(row[0]), atoi(row[2])); } EndDbQuery(); } void loadchannellist () { dbquery("SELECT * FROM `channels` WHERE `sid` = " SID ""); while((row = mysql_fetch_row(res))) { addchannel (row[1], atoi(row[0]), row[2], row[3], atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7]), row[8], atoi(row[9]), row[10], atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), atoi(row[15]), atoi(row[16]), findarmbyid(atoi(row[18])), row[17]); } EndDbQuery(); dbquery("SELECT * FROM `chanusers` WHERE `sid` = " SID " "); while((row = mysql_fetch_row(res))) { addchanuser (findchannelbyid(atoi(row[0])), atoi(row[1]), atoi(row[2]), row[3]); } EndDbQuery(); } void loadchannel (char *channame) { char query[MAX_QUERY]; sprintf(query, "SELECT * FROM `channels` WHERE name = '%s'", channame); dbquery(query); if((row = mysql_fetch_row(res))) { addchannel (row[1], atoi(row[0]), row[2], row[3], atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7]), row[8], atoi(row[9]), row[10], atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), atoi(row[15]), atoi(row[16]), findarmbyid(atoi(row[18])), row[17]); } else printf("Something relly strange heappened, the channel (%s) I tried to load doesen't exist.\n", channame); EndDbQuery(); } void joinchannels (struct arm *a) { char msg[4096], query[MAX_QUERY]; struct channel *c; strncpy(msg, "JOIN ", 4096); c = firstchan; while (c != NULL && strlen(msg) < 4096-CHANNELLEN) { if(c->arm == a) { strcat(msg, c->name); strcat(msg, ","); ++a->channels; c->ison = 0; // Assume we are NOT on the channel } c = c->next; } sprintf(query, "UPDATE `arms` SET `channels` = '%i' WHERE `id` = '%i'", a->channels, a->id); dbquery(query); EndDbQuery(); strcat(msg,"\n\r"); putserver(msg, a); } void adddbchannel (char *channame, struct arm *a) { char query[MAX_QUERY]; sprintf(query, "INSERT INTO channels (name, arm, sid) VALUES ('%s','%i','%i')", channame, a->id, atoi(SID)); dbquery(query); EndDbQuery(); } void deldbarm (char *arm) { struct arm *a; char query[MAX_QUERY]; a = findarm(arm); sprintf(query, "DELETE FROM channels WHERE arm = '%i'", a->id); dbquery(query); EndDbQuery(); sprintf(query, "DELETE FROM arms WHERE id = '%i'", a->id); dbquery(query); EndDbQuery(); } void deldbchannel (char *channame) { struct channel *c; char query[MAX_QUERY]; c = findchannel(channame); sprintf(query, "DELETE FROM channels WHERE id = '%i'", c->id); dbquery(query); EndDbQuery(); if(c->arm->type != BOT_CHECK) { sprintf(query, "DELETE FROM chanusers WHERE cid = '%i'", c->id); dbquery(query); EndDbQuery(); sprintf(query, "DELETE FROM log WHERE cid = '%d'", c->id); dbquery(query); EndDbQuery(); } } void dbchanmode (int id, char option[], char value[]) { char query[MAX_QUERY]; char *esvalue; esvalue = escapequery(value); sprintf(query, "UPDATE `channels` SET `%s` = '%s' WHERE id = '%d'", option, esvalue, id); free(esvalue); dbquery(query); EndDbQuery(); } void addlog(char *nick, char *channame, char action[10], char *msg) { struct activeuser *u; struct channel *c; u = findauser(nick); c = findchannel(channame); // printf("Starting to log for: %s channel: %s action: %s msg: %s\n", nick, channame, action, msg); if((c == NULL || (c != NULL && c->stats == 0) || u == NULL) && strcasecmp(action, "QUIT") != 0 && strcasecmp(action, "NICK") != 0) return; if(strcasecmp(action, "QUIT") != 0 && strcasecmp(action, "NICK") != 0) { log[lines].cid = c->id; strncpy(log[lines].msg, msg, 512); } else { log[lines].cid = 0; strncpy(log[lines].msg, channame, 512); } strncpy(log[lines].action, action, 10); strncpy(log[lines].nick, nick, NICKLEN); strncpy(log[lines].username, u->username, USERLEN); strncpy(log[lines].hostname, u->hostname, HOSTLEN); log[lines].uid = u->userid; log[lines].time = time(NULL); if(lines != LINESBEFORESAVE) ++lines; else flushlog(); } void flushlog() { char query[MAX_QUERY+1024]; int i = 0; char *esnick, *esusername, *eshost, *esmsg; printf("Flushing logs...\n"); while (i < lines) { esnick = escapequery(log[i].nick); esusername = escapequery(log[i].username); eshost = escapequery(log[i].hostname); esmsg = escapequery(log[i].msg); sprintf(query, "INSERT INTO `log` (`cid`, `action`, `nick`, `username`, `host`, `uid`, `msg`, `time`) VALUES ('%d', '%s', '%s', '%s', '%s', '%d', '%s', '%i')", log[i].cid, log[i].action, esnick, esusername, eshost, log[i].uid, esmsg, log[i].time); free(esnick); free(esusername); free(eshost); free(esmsg); logdbquery(query); EndLogDbQuery(); ++i; } lines = 0; } int isOpLessChan(struct channel *c) { /* This function checks if a channel is opless */ struct activechanuser *acu; if(c->isop == 1) return 0; // We are op, so the channel can't be opless acu = firstacu; while (acu != NULL) { if(acu->channel == c && acu->op == 1) return 0; // Found a guy that has op, so, why go on? acu = acu->next; } return 1; } int ServiceChannelCount (void) { /* This function returns the total number of channels that the service is on */ int channels; dbquery("SELECT COUNT(*) FROM `channels` WHERE `sid` = '" SID "'"); row = mysql_fetch_row(res); channels = atoi(row[0]); EndDbQuery(); return channels; } int OnJoinVoteMsg (struct channel *c, struct activeuser *u) { char ret[512]; if(c == NULL || c->votestruct == NULL) return 0; else { snprintf(ret, 512, "There is currently a vote going on in this channel. The question is: %c%s%c See more status and help with %c!vote status%c", BOLD, c->votestruct->question, BOLD, BOLD, BOLD); #ifdef CNOTICE cnotice(u->nick, ret); #else say(u->nick, ret, say); #endif return 1; } } int StrDurationToSeconds (char *duration) { /* Parse a string with duration and return duration in seconds */ char msg[512]; int expire; if (strstr(duration, "h") != NULL) { STR_replace_c (duration, "h", "", msg); expire = time(NULL) + atoi(msg)*3600; } else if (strstr(duration, "m") != NULL) { STR_replace_c (duration, "m", "", msg); expire = time(NULL) + atoi(msg)*60; } else if (strstr(duration, "d") != NULL) { STR_replace_c (duration, "d", "", msg); expire = time(NULL) + atoi(msg)*86400; } else { return -1; // Should be handled with something like: say(arm->parsevars->sender, "Wrong duration, duration can be day(d), hour(h), minute(m). I.e: 2h (two hours)", arm); } return expire; } int dbCount (char *query) { int count; dbquery(query); row = mysql_fetch_row(res); count = atoi(row[0]); EndDbQuery(); return count; } int AddRegexBadChan (char *regex, char *reason, int finalduration, int adder) { /* Ban a regex from request, using regular expressions */ char query[MAX_QUERY]; char *esregex, *esreason; snprintf(query, MAX_QUERY, "SELECT COUNT(*) FROM `badchan` WHERE `channel` = '%s' AND `active` = '1'", regex); if(dbCount(query) > 0) return 0; esregex = escapequery(regex); esreason = escapequery(reason); sprintf(query, "INSERT INTO `badchan` (`channel`,`adder`,`reason`,`time`, `expire`) VALUES ('%s', '%i', '%s', UNIX_TIMESTAMP(), '%i')", esregex, adder, esreason, finalduration); free(esregex); free(esreason); dbquery(query); EndDbQuery(); return 1; } int AddBadChan (char *channel, char *reason, int finalduration, int adder) { /* Ban a channel from request, apply regexp */ char regex[CHANNELLEN+2]; snprintf(regex, CHANNELLEN+2, "^%s$", channel); return AddRegexBadChan (regex, reason, finalduration, adder); } int DelBadChan (char *regex) { /* Remove regex from badchan */ char query[MAX_QUERY]; char *esregex; esregex = escapequery(regex); snprintf(query, MAX_QUERY, "SELECT COUNT(*) FROM `badchan` WHERE `channel` = '%s' AND `active` = '1'", esregex); if(dbCount(query) == 0) return 0; sprintf(query, "UPDATE `badchan` SET `active` = 0 WHERE `channel` = '%s' AND `active` = '1'", esregex); free(esregex); dbquery(query); EndDbQuery(); return 1; } void CleanUpOldBadChans (void) { /* Remove old badchans from database */ dbquery("UPDATE `badchan` SET `active` = 0 WHERE `expire` < UNIX_TIMESTAMP() AND `active` = '1'"); EndDbQuery(); } #ifdef SPAMSCAN int SOnChannel(struct channel *c) { /* Will check if SpamScan is on a channel and return 1 if it is */ if(findachanuser(SPAMSCANNICK, c->name) == NULL) return 0; else return 1; } int SpamChannel(struct channel *c) { /* Returns 1 if it is ok to spam into the channel */ if(c->nextspam <= time(NULL)) return 1; else return 0; } int SpamChannelWait(struct channel *c) { /* Adds SPAMWAIT to nextspam */ c->nextspam = time(NULL) + SPAMWAIT; return 1; } #endif void EndDbQuery(void) { /* Unlock the mutex and cleanup used resources */ if(res != NULL) mysql_free_result(res); pthread_mutex_unlock(&mysqlmutex); // UnLock mutex } void EndLogDbQuery(void) { /* Unlock the mutex and cleanup used resources */ if(res != NULL) mysql_free_result(logres); pthread_mutex_unlock(&logmysqlmutex); // UnLock mutex } void OutOfChannel(struct channel *c) { /* Execute this when the bot is out from a channel, i.e a kick or a cycle */ c->ison = 0; delallachanusers(c); }