00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef lint
00021 static const char rcsid[] = "$Id: chanserv.c,v 1.6 2005-12-28 17:27:31 sven Exp $";
00022 #endif
00023
00024 #include <eggdrop/eggdrop.h>
00025
00026 #include "chanserv.h"
00027
00028 chanserv_config_t chanserv_config;
00029 egg_server_api_t *server = NULL;
00030 static chanserv_channel_stats_t global_chan;
00031 static int timer_id;
00032 static event_owner_t chanserv_owner = {
00033 "chanserv", 0,
00034 0, 0,
00035 0
00036 };
00037
00038 static int chanserv_timer(void *client_data);
00039
00040
00041 static const char *channel_config_names[] = {
00042 "join", "part", "quit", "cycle", "leave", "ctcp", "msg",
00043 "notice", "line", "nick", "kick", "mode", "modefight",
00044 "topic"
00045 };
00046
00047 static config_var_t chanserv_config_vars[] = {
00048 {"channel.cycle_time", &chanserv_config.cycle_time, CONFIG_INT},
00049
00050 {"channel.join.period", &(chanserv_config.channel_periods[CHANSERV_STAT_JOIN]), CONFIG_INT},
00051 {"channel.join.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_JOIN]), CONFIG_INT},
00052 {"channel.part.period", &(chanserv_config.channel_periods[CHANSERV_STAT_PART]), CONFIG_INT},
00053 {"channel.part.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_PART]), CONFIG_INT},
00054 {"channel.quit.period", &(chanserv_config.channel_periods[CHANSERV_STAT_QUIT]), CONFIG_INT},
00055 {"channel.quit.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_QUIT]), CONFIG_INT},
00056 {"channel.cycle.period", &(chanserv_config.channel_periods[CHANSERV_STAT_CYCLE]), CONFIG_INT},
00057 {"channel.cycle.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_CYCLE]), CONFIG_INT},
00058 {"channel.leave.period", &(chanserv_config.channel_periods[CHANSERV_STAT_LEAVE]), CONFIG_INT},
00059 {"channel.leave.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_LEAVE]), CONFIG_INT},
00060 {"channel.ctcp.period", &(chanserv_config.channel_periods[CHANSERV_STAT_CTCP]), CONFIG_INT},
00061 {"channel.ctcp.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_CTCP]), CONFIG_INT},
00062 {"channel.msg.period", &(chanserv_config.channel_periods[CHANSERV_STAT_MSG]), CONFIG_INT},
00063 {"channel.msg.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_MSG]), CONFIG_INT},
00064 {"channel.notice.period", &(chanserv_config.channel_periods[CHANSERV_STAT_NOTICE]), CONFIG_INT},
00065 {"channel.notice.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_NOTICE]), CONFIG_INT},
00066 {"channel.line.period", &(chanserv_config.channel_periods[CHANSERV_STAT_LINE]), CONFIG_INT},
00067 {"channel.line.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_LINE]), CONFIG_INT},
00068 {"channel.nick.period", &(chanserv_config.channel_periods[CHANSERV_STAT_NICK]), CONFIG_INT},
00069 {"channel.nick.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_NICK]), CONFIG_INT},
00070 {"channel.kick.period", &(chanserv_config.channel_periods[CHANSERV_STAT_KICK]), CONFIG_INT},
00071 {"channel.kick.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_KICK]), CONFIG_INT},
00072 {"channel.mode.period", &(chanserv_config.channel_periods[CHANSERV_STAT_MODE]), CONFIG_INT},
00073 {"channel.mode.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_MODE]), CONFIG_INT},
00074 {"channel.modefight.period", &(chanserv_config.channel_periods[CHANSERV_STAT_MODEFIGHT]), CONFIG_INT},
00075 {"channel.modefight.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_MODEFIGHT]), CONFIG_INT},
00076 {"channel.topic.period", &(chanserv_config.channel_periods[CHANSERV_STAT_TOPIC]), CONFIG_INT},
00077 {"channel.topic.limit", &(chanserv_config.channel_limits[CHANSERV_STAT_TOPIC]), CONFIG_INT},
00078
00079 {0}
00080 };
00081
00082 static chanserv_channel_stats_t *chanstats_head = NULL;
00083 static bind_table_t *BT_chanflood = NULL;
00084 static bind_table_t *BT_memflood = NULL;
00085
00086 EXPORT_SCOPE int chanserv_LTX_start(egg_module_t *modinfo);
00087 static int chanserv_close(int why);
00088 static int on_chanset(const char *chan, const char *setting, const char *oldvalue, const char *newvalue);
00089
00090 static int chanserv_init()
00091 {
00092 egg_timeval_t howlong;
00093
00094 memset(&global_chan, 0, sizeof(global_chan));
00095 BT_chanflood = bind_table_add("chanflood", 5, "ssiii", MATCH_MASK, BIND_STACKABLE);
00096 BT_memflood = bind_table_add("memflood", 7, "ssssiii", MATCH_MASK, BIND_STACKABLE);
00097 server = module_get_api("server", 1, 0);
00098 bind_add_simple("chanset", NULL, "settings.chanserv.*", on_chanset);
00099 events_init();
00100 howlong.sec = CHANSERV_CLEANUP_TIMER;
00101 howlong.usec = 0;
00102 timer_id = timer_create_complex(&howlong, "chanserv stats cleanup", chanserv_timer, NULL, TIMER_REPEAT, &chanserv_owner);
00103 return(0);
00104 }
00105
00106 static int chanserv_shutdown()
00107 {
00108 bind_table_del(BT_chanflood);
00109 bind_table_del(BT_memflood);
00110 events_shutdown();
00111 return(0);
00112 }
00113
00114 int chanserv_refresh_channel_config(void *client_data)
00115 {
00116 chanserv_channel_stats_t *chanstats = client_data;
00117 channel_t *chan = server->channel_lookup(chanstats->name);
00118 int i, setting;
00119
00120 if (!chan) return(0);
00121 for (i = 0; i < CHANSERV_STAT_LEN; i++) {
00122
00123
00124
00125 if (server->channel_get_int(chan, &setting, "chanserv", 0, channel_config_names[i], 0, "period", 0, NULL)) setting = -1;
00126
00127
00128 if (setting != chanstats->periods[i]) {
00129 chanstats->periods[i] = setting;
00130 chanstats->stats[i] = 0;
00131 }
00132
00133 if (server->channel_get_int(chan, &setting, "chanserv", 0, channel_config_names[i], 0, "limit", 0, NULL)) setting = -1;
00134 chanstats->limits[i] = setting;
00135 }
00136
00137 return(0);
00138 }
00139
00140 static int on_chanset(const char *chan, const char *setting, const char *oldvalue, const char *newvalue)
00141 {
00142 chanserv_channel_stats_t *chanstats = chanserv_probe_chan(chan, 1);
00143 garbage_add(chanserv_refresh_channel_config, chanstats, GARBAGE_ONCE);
00144 return(0);
00145 }
00146
00147 chanserv_channel_stats_t *chanserv_probe_chan(const char *chan, int create)
00148 {
00149 chanserv_channel_stats_t *chanstats, *prev;
00150
00151 prev = NULL;
00152 for (chanstats = chanstats_head; chanstats; chanstats = chanstats->next) {
00153 if (!strcasecmp(chanstats->name, chan)) return(chanstats);
00154 prev = chanstats;
00155 }
00156 if (!create) return(NULL);
00157
00158
00159 chanstats = calloc(1, sizeof(*chanstats));
00160 chanstats->name = strdup(chan);
00161 chanserv_refresh_channel_config(chanstats);
00162
00163 if (prev) prev->next = chanstats;
00164 else chanstats_head = chanstats;
00165 chanstats->prev = prev;
00166 return(chanstats);
00167 }
00168
00169 chanserv_member_stats_t *chanserv_probe_member(chanserv_channel_stats_t *chan, const char *nick, const char *uhost, int create)
00170 {
00171 char *who, buf[256];
00172 int i;
00173
00174 if (!chan) chan = &global_chan;
00175
00176 who = egg_msprintf(buf, sizeof(buf), NULL, "%s!%s", nick, uhost);
00177
00178 for (i = 0; i < chan->nmembers; i++) {
00179 if (wild_match(chan->members[i].who, who)) {
00180 if (who != buf) free(who);
00181 return(chan->members+i);
00182 }
00183 }
00184 if (who != buf) free(who);
00185 if (!create) return(NULL);
00186
00187
00188 chan->members = realloc(chan->members, sizeof(*chan->members) * (chan->nmembers+1));
00189 chan->nmembers++;
00190 memset(chan->members+i, 0, sizeof(*chan->members));
00191 chan->members[i].who = ircmask_create(5, nick, uhost);
00192 return(chan->members+i);
00193 }
00194
00195 int chanserv_lookup_config(const char *chan, int stat, int *limit, int *period)
00196 {
00197 chanserv_channel_stats_t *chanstats;
00198
00199 chanstats = chanserv_probe_chan(chan, 1);
00200 if (limit) {
00201 *limit = chanstats->limits[stat];
00202 if (*limit < 0) *limit = chanserv_config.channel_limits[stat];
00203 }
00204 if (period) {
00205 *period = chanstats->periods[stat];
00206 if (*period < 0) *period = chanserv_config.channel_periods[stat];
00207 }
00208 return(0);
00209 }
00210
00211 int chanserv_update_stats(int stat, const char *chan, const char *nick, const char *uhost)
00212 {
00213 chanserv_channel_stats_t *chanstats = NULL;
00214 chanserv_member_stats_t *m = NULL;
00215 int period, limit;
00216 time_t now;
00217 int diff;
00218
00219 if (stat < 0 || stat >= CHANSERV_STAT_LEN) return(-1);
00220 if (!nick || !uhost) return(-1);
00221
00222 if (chan) chanstats = chanserv_probe_chan(chan, 1);
00223 m = chanserv_probe_member(chanstats, nick, uhost, 1);
00224 if (!m) return(-1);
00225
00226
00227
00228 now = timer_get_now_sec(NULL);
00229
00230 if (chanstats) {
00231
00232 period = chanstats->periods[stat];
00233 if (period < 0) period = chanserv_config.channel_periods[stat];
00234 if (period > 0) {
00235 limit = chanstats->limits[stat];
00236 if (limit < 0) limit = chanserv_config.channel_limits[stat];
00237
00238
00239
00240 diff = now - chanstats->last_event_time[stat];
00241 if (diff > period) {
00242 chanstats->last_event_time[stat] = now;
00243 chanstats->stats[stat] = 0;
00244 diff = 0;
00245 }
00246 chanstats->stats[stat]++;
00247
00248 if (chanstats->stats[stat] > limit) {
00249 bind_check(BT_chanflood, NULL, channel_config_names[stat], channel_config_names[stat], chan, limit, period, diff);
00250 chanstats->last_event_time[stat] = now;
00251 chanstats->stats[stat] = 0;
00252 }
00253 }
00254 }
00255
00256
00257 if (m) {
00258 m->last_event = now;
00259
00260 period = global_chan.periods[stat];
00261 if (period > 0) {
00262 limit = global_chan.limits[stat];
00263
00264
00265
00266 diff = now - m->last_event_time[stat];
00267 if (diff > period) {
00268 m->last_event_time[stat] = now;
00269 m->stats[stat] = 0;
00270 diff = 0;
00271 }
00272 m->stats[stat]++;
00273
00274 if (m->stats[stat] > limit) {
00275 bind_check(BT_memflood, NULL, channel_config_names[stat], channel_config_names[stat], chan, nick, uhost, limit, period, diff);
00276 m->last_event_time[stat] = now;
00277 m->stats[stat] = 0;
00278 }
00279 }
00280
00281 switch (stat) {
00282 case CHANSERV_STAT_LEAVE:
00283 if (now - m->last_event_time[CHANSERV_STAT_JOIN] < chanserv_config.cycle_time) {
00284 chanserv_update_stats(CHANSERV_STAT_CYCLE, chan, nick, uhost);
00285 }
00286 break;
00287 }
00288 }
00289 return(0);
00290 }
00291
00292
00293 static int cleanup_old_members(chanserv_channel_stats_t *chan)
00294 {
00295 chanserv_member_stats_t *m;
00296 time_t now;
00297 int i;
00298
00299 now = timer_get_now_sec(NULL);
00300
00301 for (i = 0; i < chan->nmembers; i++) {
00302 m = chan->members+i;
00303 if (now - m->last_event > CHANSERV_CLEANUP_TIME) {
00304 free(m->who);
00305 memmove(chan->members+i, chan->members+i+1, sizeof(*chan->members)*(chan->nmembers-i-1));
00306 chan->nmembers--;
00307 i--;
00308 }
00309 }
00310 return(0);
00311 }
00312
00313 static int free_channel(chanserv_channel_stats_t *chan)
00314 {
00315 int i;
00316
00317
00318 putlog(LOG_MISC, "*", "chanserv: freeing channel '%s'", chan->name);
00319 free(chan->name);
00320 for (i = 0; i < chan->nmembers; i++) {
00321 free(chan->members[i].who);
00322 }
00323 free(chan->members);
00324 if (chan->prev) chan->prev->next = chan->next;
00325 else chanstats_head = chan->next;
00326 if (chan->next) chan->next->prev = chan->prev;
00327 free(chan);
00328 return(0);
00329 }
00330
00331 static int chanserv_timer(void *client_data)
00332 {
00333 chanserv_channel_stats_t *chan, *next;
00334
00335 for (chan = chanstats_head; chan; chan = next) {
00336 next = chan->next;
00337 if (!server->channel_lookup(chan->name)) free_channel(chan);
00338 else cleanup_old_members(chan);
00339 }
00340 cleanup_old_members(&global_chan);
00341 return(0);
00342 }
00343
00344 static int chanserv_close(int why)
00345 {
00346 void *config_root;
00347
00348 chanserv_shutdown();
00349 config_root = config_get_root("eggdrop");
00350 config_unlink_table(chanserv_config_vars, config_root, "chanserv", 0, NULL);
00351 return(0);
00352 }
00353
00354 int chanserv_LTX_start(egg_module_t *modinfo)
00355 {
00356 void *config_root;
00357
00358 chanserv_owner.module = modinfo;
00359
00360 modinfo->name = "chanserv";
00361 modinfo->author = "eggdev";
00362 modinfo->version = "1.0.0";
00363 modinfo->description = "channel services";
00364 modinfo->close_func = chanserv_close;
00365
00366
00367 memset(&chanserv_config, 0, sizeof(chanserv_config));
00368
00369
00370 config_root = config_get_root("eggdrop");
00371 config_link_table(chanserv_config_vars, config_root, "chanserv", 0, NULL);
00372 config_update_table(chanserv_config_vars, config_root, "chanserv", 0, NULL);
00373
00374 chanserv_init();
00375 return(0);
00376 }