#include "module.h" #define AUTHOR "Rob" #define VERSION "0.6" /* Default database name */ #define DEFAULT_DB_NAME "cs_limit.db" /* Multi-language stuff */ #define LANG_NUM_STRINGS 5 #define LIMIT_SYNTAX 0 #define LIMIT_ADD_SUCCESS 1 #define LIMIT_DEL_SUCCESS 2 #define LIMIT_HELP 3 #define LIMIT_HELP_MAIN 4 /** * User interface calls. **/ int do_set_limit(User * u); /** * Channel management calls. **/ int is_managed_channel(char *name); void managed_add_channel(User *u, char *name, int buffer, int timeout); void managed_add_channel_str(User *u, char *name, char *buffer, char *timeout); void managed_del_channel(User *u, char *name); int managed_get_timeout(char *name); int managed_get_buffer(char *name); /** * Event hooks. **/ int do_part_channel(int argc, char **argv); int do_quit_channel(int argc, char **argv); int do_kick_channel(int argc, char **argv); int do_join_channel(int argc, char **argv); int do_chan_registered(int argc, char **argv); /** * Mode managment calls **/ int do_mode_change(int delay, char *channel, int new_limit); int do_mode_change_delayed(int argc, char **argv); int do_mode_change_now(char *channel, int new_limit); int do_mode_change_get_current_limit(ChannelInfo *channel); void do_mode_change_set_current_limit(Channel *channel, int limit); void do_mode_change_find_mode(); int do_mode_change_send(char *channel, int new_limit); /** * Language Managment / Help calls. **/ void mAddLanguages(void); int mCSHelp(User * u); void mMainCSHelp(User * u); /** * Database/Config file management calls. **/ int mLoadData(void); int mSaveData(int argc, char **argv); int mLoadConfig(); int mEventReload(int argc, char **argv); /** * Ugly but useful globals. **/ char ircd_mode_change_char = 0; char *CSLimitDBName = NULL; int operOnly = 0; int defaultBuffer = 0; int defaultTimeout = 0; /** * Main module init routine. * @param argc Argument count. * @param argv Argument list. **/ int AnopeInit(int argc, char **argv) { int status = 0; EvtHook *hook = NULL; Command *c = NULL; hook = createEventHook( EVENT_JOIN_CHANNEL, do_join_channel); status += moduleAddEventHook(hook); hook = createEventHook( EVENT_PART_CHANNEL, do_part_channel); status += moduleAddEventHook(hook); hook = createEventHook( EVENT_CHAN_KICK, do_kick_channel); status += moduleAddEventHook(hook); hook = createEventHook( EVENT_USER_LOGOFF, do_quit_channel); status += moduleAddEventHook(hook); hook = createEventHook( EVENT_RELOAD, mEventReload); status += moduleAddEventHook(hook); hook = createEventHook(EVENT_DB_SAVING, mSaveData); status += moduleAddEventHook(hook); hook = createEventHook(EVENT_CHAN_REGISTERED,do_chan_registered); status += moduleAddEventHook(hook); c = createCommand("limit", do_set_limit, NULL, -1, -1, -1, -1, -1); moduleAddHelp(c, mCSHelp); status += moduleAddCommand(CHANSERV, c, MOD_HEAD); if(status!=0) { alog("cs_limit: Something didnt init. right"); return MOD_STOP; } moduleSetChanHelp(mMainCSHelp); do_mode_change_find_mode(); mAddLanguages(); mLoadConfig(); mLoadData(); moduleAddAuthor(AUTHOR); moduleAddVersion(VERSION); return MOD_CONT; } /** * Anope unload routine. **/ void AnopeFini(void) { if (CSLimitDBName) free(CSLimitDBName); } /** * argc: 3 * argv[0]: EVENT_START / EVENT_STOP * argv[1]: User Nick * argv[2]: Channel **/ int do_join_channel(int argc, char **argv) { if(strcmp(EVENT_STOP,argv[0])==0) { if(is_managed_channel(argv[2])) { do_mode_change(managed_get_timeout(argv[2]),argv[2],0); } } return MOD_CONT; } /** * argc: 3 * argv[0]: EVENT_START / EVENT_STOP * argv[1]: User Nick * argv[2]: Channel **/ int do_part_channel(int argc, char **argv) { if(strcmp(EVENT_STOP,argv[0])==0) { if(is_managed_channel(argv[2])) { do_mode_change(0,argv[2],0); } } return MOD_CONT; } /** * argc: 2 * argv[0]: User Nick * argv[1]: Channel **/ int do_kick_channel(int argc, char **argv) { if(is_managed_channel(argv[1])) { do_mode_change(0,argv[1],-1); } return MOD_CONT; } /** * argc: 1 * argv[0]: Channel Name **/ int do_chan_registered(int argc, char **argv) { if( (defaultBuffer > 0) && (defaultTimeout > 0) ) { managed_add_channel(NULL, argv[0], defaultBuffer, defaultTimeout); } return MOD_CONT; } /** * argc: 1 * argv[0]: User Nick **/ int do_quit_channel(int argc, char **argv) { struct u_chanlist *c=NULL; User *user=NULL; user = finduser(argv[0]); /* find the user record for the nick who is leaving */ c = user->chans; while (c) { /* Walk the departing users chan list, looking for channels we manage */ if(is_managed_channel(c->chan->name)) { /* TODO: make it work right here - anope hasnt took the user away yet, so it dosnt change the modes when they leave */ do_mode_change(0,c->chan->name,-1); } c = c->next; } return MOD_CONT; } /** * Change the mode of a given channel, taking the delay into account. * @param delay Time in seconds for the mode change. * @param channel Channel to change the modes for. * @param new_limit Modifiter to change the channel limit. **/ int do_mode_change(int delay, char *channel, int modifier) { char my_time[4]; char name[100]; char newlimit[4]; char *argv[2]; ChannelInfo *ci=NULL; int current_limit = 0; if(! (ci=cs_findchan(channel))) { alog("cs_limit: Strange error, trying to change modes for non-registered channel!"); return 0; } current_limit = do_mode_change_get_current_limit(ci) + modifier; snprintf(newlimit,sizeof(newlimit),"%d",current_limit); snprintf(my_time,sizeof(my_time),"%d",delay); snprintf(name,sizeof(name),"%s",channel); argv[0] = channel; argv[1] = newlimit; moduleDelCallback(name); if(delay==0) { moduleAddCallback(name,time(NULL)+2,do_mode_change_delayed,2,argv); // do_mode_change_now(channel,current_limit); } else { moduleAddCallback(name,time(NULL)+dotime(my_time),do_mode_change_delayed,2,argv); } return MOD_CONT; } /** * Change the modes of a given channel using a delay * @param argc - Argument count * @param argv[0] Channel * @param argv[1] New Limit **/ int do_mode_change_delayed(int argc, char **argv) { int limit = 0; limit = argv[1] ? strtoul(argv[1], NULL, 10) : 0; do_mode_change_send(argv[0],limit); return MOD_CONT; } /** * Change the modes of a channel right now. * @param channel The name of the channel * @param new_limit The new limit for the channel **/ int do_mode_change_now(char *channel, int new_limit) { do_mode_change_send(channel,new_limit); return MOD_CONT; } /** * Get the current "artificial" channel limit for a channel. * If we cant get the artificial limit, just use the real one. * @param ci The channel info struct for the channel * @return The current channel limit, 0 for no limit. **/ int do_mode_change_get_current_limit(ChannelInfo *ci) { char *tmp=NULL; int ret=0; int buff=0; if(ci && ci->c) { ret = ci->c->usercount; if( (tmp = moduleGetData(&ci->moduleData,"buffer")) ) { buff = strtoul(tmp, NULL, 10); if(debug){ alog("cs_limit do_mode_change_get_current_limit: current limit [%d], buffer [%d], new [%d]",ret,buff,(ret+buff)); } free(tmp); } } return ret+buff; } /** * Set the current channel limit for a channel. * @param channel The channel to set the limit for. **/ void do_mode_change_set_current_limit(Channel *channel, int limit) { if(channel){ channel->limit = limit; } } /** * Actaully send a mode change to the ircd, keeping anope's records upto date. * @param channel The channel we want to set the modes for. * @param new_limit The new limit we want to set. **/ int do_mode_change_send(char *channel, int new_limit) { ChannelInfo *ci=NULL; if((ci = cs_findchan(channel)) && ci->c ) { anope_cmd_mode(whosends(ci), channel,"+%c %d", ircd_mode_change_char, new_limit); do_mode_change_set_current_limit(ci->c,new_limit); } return MOD_CONT; } /** * Find out which character we need to send to the ircd in order to set a limit on a channel. **/ void do_mode_change_find_mode() { CBModeInfo *cbmi = cbmodeinfos; int found = 0; int limit_mode = anope_get_limit_mode(); do { if( cbmi->flag == limit_mode) { ircd_mode_change_char = cbmi->mode; found = 1; } } while( ((++cbmi)->flag != 0) && !found ); } /** * Same as managed_add_channel only accepts char * as buffer/timeout values **/ void managed_add_channel_str(User *u, char *name, char *buffer, char *timeout) { int iBuffer=0; int iTimeout=0; iBuffer = strtoul(buffer, NULL, 10); iTimeout = strtoul(timeout, NULL, 10); managed_add_channel(u,name,iBuffer,iTimeout); } /** * Add a channel to our list of managed channels, using the given buffer and given timeout. * If the channel is already considered "manageded" change settings to use the new values. * @param u The user issuing the command, NULL if there is no user. * @param name Name of the channel to manage. * @param buffer The buffer zone for new clients joining. * @param timeout The timeout before increasing the limit. **/ void managed_add_channel(User *u, char *name, int buffer, int timeout) { ChannelInfo *ci = NULL; char buff[16]; char timeou[16]; if(buffer == 0 || timeout == 0 ) { if(u) { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } return; } if( (ci = cs_findchan(name))) { if(u) { if(operOnly) { if(!is_oper(u)) { notice_lang(s_ChanServ, u, ACCESS_DENIED); return; } } else { if(!is_founder(u, ci)) { notice_lang(s_ChanServ, u, ACCESS_DENIED); return; } } } snprintf(buff, sizeof(buff), "%d", buffer); snprintf(timeou, sizeof(timeou), "%d", timeout); moduleAddData(&ci->moduleData,"buffer",buff); moduleAddData(&ci->moduleData,"timeout",timeou); if(debug) { alog("cs_limit: Added Data for channel [%s], Buffer [%s], timeout [%s]",name,buff,timeou); } if(u) { moduleNoticeLang(s_ChanServ, u, LIMIT_ADD_SUCCESS, name, buffer, timeout); } if(ci->c) { do_mode_change_now(name,ci->c->usercount+buffer); } } else { if(u) { notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED,name); } } } /** * Stop managaing a channel. * @param name The name of the channel to stop managing. **/ void managed_del_channel(User *u, char *name) { ChannelInfo *ci = NULL; if( (ci = cs_findchan(name)) ) { if(u) { if(operOnly) { if(!is_oper(u)) { notice_lang(s_ChanServ, u, ACCESS_DENIED); return; } } else { if(!is_founder(u, ci)) { notice_lang(s_ChanServ, u, ACCESS_DENIED); return; } } } moduleDelData(&ci->moduleData,"buffer"); moduleDelData(&ci->moduleData,"timeout"); moduleNoticeLang(s_ChanServ, u, LIMIT_DEL_SUCCESS, name); } else { notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED,name); } } /** * Get the current timeout for the given channel, 0 for none. * @param name The name of the channel to find the timeout for. * @return The timeout for the given channel, 0 for none. **/ int managed_get_timeout(char *name) { int timeout=0; char *md = NULL; ChannelInfo *ci = NULL; if( (ci = cs_findchan(name)) ) { if( (md = moduleGetData(&ci->moduleData,"timeout")) ) { timeout = strtoul(md, NULL, 10); free(md); } } return timeout; } /** * Get the current buffer for the given channel, 0 for none. * @param name The name of the channel to find the buffer for. * @return The buffer for the given channel, 0 for none. **/ int managed_get_buffer(char *name) { int buffer=0; char *md = NULL; ChannelInfo *ci = NULL; if( (ci = cs_findchan(name)) ) { if( (md = moduleGetData(&ci->moduleData,"buffer")) ) { buffer = strtoul(md, NULL, 10); free(md); } } return buffer; } /** * Check if the channel given is one that we manage. * @param name The name of the channel to check. * @return 1 if the channel is managed, 0 if it isnt. **/ int is_managed_channel(char *name) { ChannelInfo *ci = NULL; char *md=NULL; if( (ci = cs_findchan(name)) ) { if( (md = moduleGetData(&ci->moduleData,"buffer")) ) { free(md); return 1; } } return 0; } /** * Add module langauge strings to anope's module langauge system. * This will allow users to get help in there own langauge. **/ void mAddLanguages(void) { char *langtable_en_us[] = { "Syntax: LIMIT [ADD | DEL ]", "Now limiting channel %s (%d:%d)", "No longer limiting channel %s", "Syntax: LIMIT [ADD | DEL ]\n" "Add or Delete automatic channel limiting for the given channel\n" "where buffer is the number to set the channel limit to\n" "and timeout is the delay before increasing it.", " LIMIT Add / Del automatic channel limiting" }; moduleInsertLanguage(LANG_EN_US, LANG_NUM_STRINGS, langtable_en_us); } /** * Display help to the user when they /cs help limit * @param u The user requesting help * @return Should control carry on, MOD_CONT or MOD_STOP. **/ int mCSHelp(User * u) { moduleNoticeLang(s_ChanServ, u, LIMIT_HELP); return MOD_CONT; } /** * Display limit as a valid chanserv command on /cs help * @param u The user requesting help **/ void mMainCSHelp(User * u) { moduleNoticeLang(s_ChanServ, u, LIMIT_HELP_MAIN); } /** * Load any info from our database file populating the module data feilds. **/ int mLoadData(void) { FILE *in; char *c_channel = NULL; char *c_buffer = NULL; char *c_timeout = NULL; ChannelInfo *ci = NULL; int iBuffer=0, iTimeout=0; char buffer[2000]; int len=0; if ((in = fopen(CSLimitDBName, "r")) == NULL) { alog("cs_limit: WARNING: can not open the database file! (it might not exist yet. This is not fatal!)"); } else { while (fgets(buffer, 1500, in)) { c_channel = myStrGetToken(buffer, ' ', 0); c_buffer = myStrGetToken(buffer, ' ', 1); c_timeout = myStrGetTokenRemainder(buffer, ' ', 2); if(c_channel) { if(c_buffer) { if(c_timeout) { len = strlen(c_timeout); /* Take the \n from the end of the line */ if(c_timeout[len-1]=='\n') c_timeout[len-1] = '\0'; if ((ci = cs_findchan(c_channel))) { iBuffer = strtoul(c_buffer, NULL, 10); iTimeout = strtoul(c_timeout, NULL, 10); managed_add_channel(NULL,c_channel,iBuffer,iTimeout); } free(c_timeout); } free(c_buffer); } free(c_channel); } } } return MOD_CONT; } /** * Save details of any channel using the auto limiter into our database file. **/ int mSaveData(int argc, char **argv) { ChannelInfo *ci = NULL; char *c_buffer = NULL; char *c_timeout = NULL; FILE *out; int i=0; if (argc >= 1) { if (!stricmp(argv[0], EVENT_START)) { if ((out = fopen(CSLimitDBName, "w")) == NULL) { alog("cs_limit: ERROR: can not open the database file!"); anope_cmd_global(s_OperServ,"cs_limit: ERROR: can not open the database file!"); } else { for (i = 0; i < 256; i++) { for (ci = chanlists[i]; ci; ci = ci->next) { c_buffer = moduleGetData(&ci->moduleData,"buffer"); if(c_buffer) { c_timeout = moduleGetData(&ci->moduleData,"timeout"); if(c_timeout) { fprintf(out,"%s %s %s\n",ci->name,c_buffer,c_timeout); free(c_timeout); } free(c_buffer); } } } fclose(out); } } } return MOD_CONT; } /** * Load our config data from the config file. **/ int mLoadConfig() { char *tmp = NULL; int i; Directive directivas[][1] = { {{"CSLimitDBName", {{PARAM_STRING, PARAM_RELOAD, &tmp}}}}, {{"CSLimitOperOnly", {{PARAM_INT, PARAM_RELOAD, &operOnly}}}}, {{"CSLimitDefaultBuffer", {{PARAM_INT, PARAM_RELOAD, &defaultBuffer}}}}, {{"CSLimitDefaultTimeout", {{PARAM_INT, PARAM_RELOAD, &defaultTimeout}}}} }; for (i = 0; i < 4; i++) moduleGetConfigDirective(directivas[i]); if (CSLimitDBName) free(CSLimitDBName); if (tmp) { CSLimitDBName = tmp; } else { CSLimitDBName = sstrdup(DEFAULT_DB_NAME); alog("cs_limit: CSLimitDBName is not defined in Services configuration file, using default %s", CSLimitDBName); } alog("cs_limit: Config: Database [%s], OperOnly: [%d], DefaultBuffer: [%d], DefaultTimeout: [%d]",CSLimitDBName,operOnly,defaultBuffer,defaultTimeout); return 0; } /** * The user has attempted to reload the config file, check to see if * any of our config directives have changed. **/ int mEventReload(int argc, char **argv) { int ret = 0; if (argc >= 1) { if (!stricmp(argv[0], EVENT_START)) { alog("cs_limit: Reloading configuration directives..."); ret = mLoadConfig(); } } if (ret) alog("cs_limit: ERROR: An error has occured while reloading the configuration file"); return MOD_CONT; } /** * Provide a nice interface to the user, allowing add/del of auto-limiting. * @param u The user using the command **/ int do_set_limit(User * u) { char *text=NULL; char *cmd=NULL; char *channel=NULL; char *buffer=NULL; char *timeout=NULL; text = moduleGetLastBuffer(); if(text) { cmd = myStrGetToken(text, ' ', 0); if(strcasecmp(cmd,"ADD")==0) { channel = myStrGetToken(text, ' ',1); if(channel) { buffer = myStrGetToken(text, ' ', 2); if(buffer) { timeout = myStrGetTokenRemainder(text, ' ', 3); if(timeout) { managed_add_channel_str(u,channel,buffer,timeout); free(timeout); } else { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } free(buffer); } else { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } free(channel); } else { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } } else if(strcasecmp(cmd,"DEL")==0) { channel = myStrGetToken(text, ' ',1); if(channel) { managed_del_channel(u,channel); free(channel); } else { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } } else { moduleNoticeLang(s_ChanServ, u, LIMIT_SYNTAX); } } return MOD_CONT; } /* EOF */