diff -Nur src/mod/gseen.mod/Makefile src/mod/gseen.mod/Makefile --- ./src/mod/gseen.mod/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/Makefile 2002-10-26 13:17:44.000000000 +0200 @@ -0,0 +1,28 @@ +# Makefile for src/mod/gseen.mod/ + +doofus: + @echo "" + @echo "Let's try this from the right directory..." + @echo "" + @cd ../../../; make + +clean: + @rm -f *.o *.$(MOD_EXT) *~ + +static: ../gseen.o + +modules: ../../../gseen.$(MOD_EXT) + +../gseen.o: ../module.h ../modvals.h ../../eggdrop.h datahandling.c \ + gseen.c sensors.c gseencmds.c gseencmds.c do_seen.c ai.c tclcmds.c \ + misc.c seentree.c generic_binary_tree.c slang_gseen_commands.c \ + slang.c slang_text.c slang_ids.c slang_chanlang.c seenlang.h \ + slang_multitext.c gseen.h + $(CC) $(CFLAGS) $(CPPFLAGS) -DMAKING_MODS -c gseen.c + rm -f ../gseen.o + mv gseen.o ../ + +../../../gseen.$(MOD_EXT): ../gseen.o + $(LD) -o ../../../gseen.$(MOD_EXT) ../gseen.o $(XLIBS) + +#safety hash diff -Nur src/mod/gseen.mod/README src/mod/gseen.mod/README --- ./src/mod/gseen.mod/README 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/README 2002-10-26 13:17:45.000000000 +0200 @@ -0,0 +1,140 @@ +Description: +------------ + +gseen.mod is a seen module for eggdrop that tracks not only the users in the +bot's userfile, but everyone who enters one of the bots channels. +It does pretty much the same as the popular script bseen and has a few +additional features like AI-seen and seen-notification. +It's also way faster than any corresponding script because scripts are always +much slower than modules. Especially scripts that deal with large amount of +data often become incredible slow. + +Installation: +------------- + +gseen.mod is written for eggdrop1.6, but it should also work with eggdrop 1.4. + +You need the eggdrop source to compile the module. + +The following instructions assume, ~/eggdrop1.6.2/ is the directory +where you installed your eggdrop from. (of course, other source dirs +will work as well) + +Put gseen.mod.1.1.0.tar.gz in ~/eggdrop1.6.2/src/mod/, +and unpack it (tar xfz gseen.mod.1.1.0.tar.gz). Change directory +back to ~/eggdrop1.6.2/. + +Now just do what you've done when you compiled your bot: +"./configure" +"make config" (you can skip this command on eggdrop 1.4) +"make" +"make install" + +Don't forget to copy the langfiles from eggdrop1.6.2/src/mod/gseen.mod/ to +eggdrop/language. + +All settings can be found in ~/eggdrop1.6.2/src/mod/gseen.mod/gseen.conf +Copy it to your eggdrop directory, edit it to fit your needs and put +"source gseen.conf" at the end of your eggdrop config file. The last thing +to do is to .rehash your bot. + + +Public commands: +---------------- + +!seen + I think this command doesn't need an explanation. ^_^ +!seen + Searches the database for entries that match + for example "!seen *!user@dialin-*.isp.com" +!seennick + !seen also checks if a user was online later with a + different nick. !seennick only seens for +!seenstats + just a little report on how many nicks are tracked + +All commands are also accessible via /msg. +("/msg seen ", for example) + + +AI seen: +-------- + +This module has a simple built in AI routine. +A short example: + + Argo: have you seen Fabian recently? +<|Argo|> G`Quann, fabian (~fabian@dns.gifs.de) was last seen quitting +from #eggdev 1 week 4 days 9 hours 40 minutes 56 seconds ago +(20.02. 01:39) stating ".....zzzzZZZzzZZZzZZZZZZZZZZzzz..". + +Well, it's not a very intelligent AI, it's rather brute-force. So don't +forget to use the ai-seen-ignore setting. +I know that's not coded very elegant, but if you configure it correctly, +the failure-rate is way lower than with other AI scripts... + +DCC commands: +------------- + +.seen +.seennick +.seenstats + just the same as the public versions +.purgeseens + deletes expired data (this also happens automatically once a day) + (m) + +Channel Settings: +----------------- + + +noseendata + don't log any seen data in this channel + +quietseens + send answers directly via notice to the person who asked and + don't bother the rest of the channel with the reply + +quietaiseens + same as +quietseens, but for AI seens + +nopubseens + ignore every seen-command in this channel + +TCL commands: +------------- + +There are no special tcl commands, only the usual bind procs. + +The only one that should be mentioned is: + +*pubm:seen + triggers the AI seen + returns: 1 if a reply was sent, 0 otherwise + +So if you're using another AI script on your bot, you can modify it to +use this proc and avoid doubled replies this way. + +Other: +------ + +There is absolutely NO WARRANTY on this module. I do my best to make it +work properly, but if anything gets screwed up, I'm not responsible. Use +this module at your own risk. + +Feedback: +--------- + +Feel free to send feedback and bugreports (I hope there won't be any) to +gseen.mod@visions-of-fantasy.de + +The newest gseen version can always be found at: +http://www.visions-of-fantasy.de/gseen.mod/ + +Thanks to: +---------- + +- Fabian for teaching me plenty of things +- everyone who tested the many buggy development versions :) +- the eggdev team for developing eggdrop + +Most of all, I would like to thank Bass for writing bseen.tcl because alot +of the ideas for this module came from using that tcl script. It's still the +most powerful seen script, so if you want something that's easier to use than +a module, get a copy of bseen.tcl. diff -Nur src/mod/gseen.mod/UPDATES src/mod/gseen.mod/UPDATES --- ./src/mod/gseen.mod/UPDATES 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/UPDATES 2002-10-26 13:17:46.000000000 +0200 @@ -0,0 +1,55 @@ +Changes in gseen.mod: (since v1.0.0) +-------------------- + +1.1.1 +- fixed "no newline" compilation warnings that appeared on some systems. +- fixed uninitialized "li" variable in do_seen() +- fixed lacking compatibility to eggdrop1.4 (confirmation anyone?) +- new option: hide-secret-chans + +1.1.0 (15.6.2001) +- added multilang support +- removed static buffers +- organized data in a binary search tree (much faster) +- optimized a few other things +- added settings: + - fuzzy-search + - max-matches + - wildcard-search + +1.0.8 +- quiet-seens wasn't working for !seennick +- added quiet-ai-seens +- renamed nopub to nopubseens and nolog to noseendata and + quietseen to quietseens + +1.0.7 +- added compatibility to !channels +- fixed a bug relating strict-host 0 had some strange effects on + !seen requests for users with ~ in their ident + +1.0.6 +- fixed a very evil bug that allowed anyone to crash the bot, sorry + +1.0.5 +- quietseens wasn't working correctly +- added support for egg1.5's udef chansets + +1.0.4 +- added GPL stuff +- changed error msg that appears if no gseen file exists + +1.0.3 +- readme updates +- fixed a grammatical error in do_seen + +1.0.2 +- bot wanted to free a NULL pointer sometimes + +1.0.1 +- !seen without parameter returned stupid results :) +- fixed little typo in .purgeseens +- "I found 1 matches..." -> "I found 1 match..." + +1.0.0 +- release :) diff -Nur src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl --- ./src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl 2002-10-26 13:18:14.000000000 +0200 @@ -0,0 +1,80 @@ +##################################################################### +# +# gseen.selectlang v1.0.0 +# +# This is a simple script which selects a language based on the +# user's host. +# +# It only works for /msg commands. +# +# If the user is in a channel which has a language defined, gseen's +# internal functions will override this selection and use the language +# of the channel instead. +# +##################################################################### + + +# Here you can define which language to use for which host. +# The first part is the mask for the host, and the second part +# is the language which should be used for this host. + +set tld-langs { + {"*.de" "de"} + {"*.at" "de"} + {"*.ch" "de"} + {"*.t-dialin.net" "de"} + {"*.t-ipconnect.net" "de"} + {"*.pl" "pl"} + {"*.jp" "ja"} +} + +################################################# + + +proc selectlang:getlang {uhost} { + global tld-langs + + foreach tld ${tld-langs} { + if {[string match [lindex $tld 0] $uhost]} { + return [lindex $tld 1] + } + } + return "" +} + +proc sl:rebind {oldtarget newtarget} { + foreach binding [binds msg] { + if {[lindex $binding 4] == $oldtarget} { + unbind [lindex $binding 0] [lindex $binding 1] [lindex $binding 2] [lindex $binding 4] + bind [lindex $binding 0] [lindex $binding 1] [lindex $binding 2] $newtarget + } + } +} + +proc sl:msg:trigger {nick uhost hand rest target} { + global default-slang + + set lang [selectlang:getlang $uhost] + set old-slang ${default-slang} + if {$lang != ""} { + set default-slang $lang + putlog "using '$lang'..." + } + $target $nick $uhost $hand $rest + set default-slang ${old-slang} +} + +sl:rebind *msg:seen sl:msg:seen +proc sl:msg:seen {nick uhost hand rest} { + sl:msg:trigger $nick $uhost $hand $rest *msg:seen +} + +sl:rebind *msg:seenstats sl:msg:seenstats +proc sl:msg:seenstats {nick uhost hand rest} { + sl:msg:trigger $nick $uhost $hand $rest *msg:seenstats +} + +sl:rebind *msg:seennick sl:msg:seennick +proc sl:msg:seennick {nick uhost hand rest} { + sl:msg:trigger $nick $uhost $hand $rest *msg:seennick +} \ No newline at end of file diff -Nur src/mod/gseen.mod/ai.c src/mod/gseen.mod/ai.c --- ./src/mod/gseen.mod/ai.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/ai.c 2002-10-26 13:17:47.000000000 +0200 @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static int quietaiseens(char *chan) +{ + char buf[121], *b; + + Context; + strncpy(buf, quiet_ai_seen, 120); + buf[120] = 0; + b = buf; + while (b[0]) + if (!strcasecmp(chan, newsplit(&b))) + return 1; +#if EGG_IS_MIN_VER(10503) + if (ngetudef("quietaiseens", chan)) + return 1; +#endif + return 0; +} + +static int tcl_pubmseen STDVAR +{ + char *nick, *uhost, *hand, *chan, *text; + char buf[1024]; + char *words, *word; + seendat *l; + int i; + + Context; + BADARGS(6, 6, " nick uhost hand chan text"); + nick = argv[1]; + uhost = argv[2]; + hand = argv[3]; + chan = argv[4]; + text = argv[5]; + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan)); + glob_nick = nick; + for (i = 0; i < strlen(text); i++) + if (strchr("!?.,\"", text[i])) + text[i] = ' '; + strncpy(buf, ignore_words, 1023); + buf[1023] = 0; + words = buf; + while (words[0]) + add_ignoredword(newsplit(&words)); + strncpy(buf, text, 1023); + buf[1023] = 0; + words = buf; + while (words[0]) { + word = newsplit(&words); + if (word_is_ignored(word)) + continue; + l = findseen(word); + if (l) { + if (quietaiseens(chan)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, + do_seen(word, nick, uhost, chan, 0)); + } else { + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", chan, reply_prefix, + do_seen(word, nick, uhost, chan, 0)); + } + add_seenreq(word, nick, uhost, chan, now); + free_ignoredwords(); + Tcl_AppendResult(irp, "1", NULL); + return TCL_OK; + } + } + free_ignoredwords(); + Tcl_AppendResult(irp, "0", NULL); + return TCL_OK; +} + +static tcl_cmds mytcls[] = +{ + {"*pubm:seen", tcl_pubmseen}, + {"*chjn:gseen", gseen_chjn}, + {"*chpt:gseen", gseen_chpt}, + {0, 0} +}; + +static void add_ignoredword(char *word) +{ + ignoredword *l, *nl; + + l = ignoredwords; + while (l && l->next) + l = l->next; + nl = nmalloc(sizeof(ignoredword)); + nl->word = nmalloc(strlen(word) + 1); + strcpy(nl->word, word); + nl->next = NULL; + if (ignoredwords) + l->next = nl; + else + ignoredwords = nl; +} + +static void free_ignoredwords() +{ + ignoredword *l, *ll; + + l = ignoredwords; + while (l) { + ll = l->next; + nfree(l->word); + nfree(l); + l = ll; + } + ignoredwords = NULL; +} + +static int expmem_ignoredwords() +{ + ignoredword *l; + int size = 0; + + for (l = ignoredwords; l; l = l->next) { + size += sizeof(ignoredword); + size += strlen(l->word) + 1; + } + return size; +} + +static int word_is_ignored(char *word) +{ + ignoredword *l; + + for (l = ignoredwords; l; l = l->next) + if (!strcasecmp(l->word, word)) + return 1; + return 0; +} diff -Nur src/mod/gseen.mod/datahandling.c src/mod/gseen.mod/datahandling.c --- ./src/mod/gseen.mod/datahandling.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/datahandling.c 2002-10-26 13:17:48.000000000 +0200 @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +static void write_seens() +{ + seenreq *r; + seenreq_by *b; + FILE *f; + char s[125]; + + Context; + /* putlog(LOG_MISC, "*", "Saving seen data..."); */ + if (!gseenfile[0]) + return; + sprintf(s, "%s~new", gseenfile); + f = fopen(s, "w"); + chmod(s, 0600); + if (f == NULL) { + putlog(LOG_MISC, "*", "ERROR writing gseen file."); + return; + } + fprintf(f, "# gseen data file v1.\n"); + write_seen_tree_target = f; + btree_getall(&seentree, write_seen_tree); + for (r = requests; r; r = r->next) + for (b = r->by; b; b = b->next) + /* @ nick by host chan when */ + fprintf(f, "@ %s %s %s %s %lu\n", r->nick, b->who, b->host, b->chan, + b->when); + fclose(f); + unlink(gseenfile); + movefile(s, gseenfile); + /* putlog(LOG_MISC, "*", "Done."); */ + return; +} + +static void read_seens() +{ + FILE *f; + char buf[512], *s, *type, *nick, *host, *chan, *msg, *by; + time_t when; + int spent, iType, i; + + Context; + f = fopen(gseenfile, "r"); + if (f == NULL) { + putlog(LOG_MISC, "*", "Can't open gseen file, creating new database..."); + return; + } + while (!feof(f)) { + buf[0] = 0; + s = buf; + fgets(s, 511, f); + i = strlen(buf); + if (buf[i - 1] == '\n') + buf[i - 1] = 0; + if ((buf[0] == 0) || (buf[0] == '#')) + continue; + type = newsplit(&s); + if (!strcmp(type, "!")) { + nick = newsplit(&s); + host = newsplit(&s); + chan = newsplit(&s); + iType = atoi(newsplit(&s)); + when = (time_t) atoi(newsplit(&s)); + spent = atoi(newsplit(&s)); + msg = s; + add_seen(iType, nick, host, chan, msg, when, spent); + } else if (!strcmp(type, "@")) { + nick = newsplit(&s); + by = newsplit(&s); + host = newsplit(&s); + chan = newsplit(&s); + when = (time_t) atoi(newsplit(&s)); + add_seenreq(nick, by, host, chan, when); + } + } + fclose(f); + Context; + return; +} + +static void purge_seens() +{ + seenreq *r, *rr; + seenreq_by *b, *bb; + + Context; + if (!expire_seens) + return; + btree_getall_expanded(&seentree, purge_seen_tree); + debug0("purge done"); + r = requests; + rr = NULL; + while (r) { + b = r->by; + bb = NULL; + while (b) { + if ((now - b->when) > (expire_seens * 86400)) { + debug2("request for %s from %s has expired.", r->nick, b->who); + nfree(b->who); + nfree(b->host); + nfree(b->chan); + if (bb) { + bb->next = b->next; + nfree(b); + b = bb->next; + } else { + r->by = b->next; + nfree(b); + b = r->by; + } + } else { + bb = b; + b = b->next; + } + } + if (!r->by) { + debug1("no further seen requests for %s, deleting", r->nick); + nfree(r->nick); + if (rr) { + rr->next = r->next; + nfree(r); + r = rr->next; + } else { + requests = r->next; + nfree(r); + r = requests; + } + } else { + rr = r; + r = r->next; + } + } +} diff -Nur src/mod/gseen.mod/do_seen.c src/mod/gseen.mod/do_seen.c --- ./src/mod/gseen.mod/do_seen.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/do_seen.c 2002-10-26 13:17:50.000000000 +0200 @@ -0,0 +1,840 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* do_seen(): Checks if someone matches the mask, and returns the reply + * mask : first paramater (e.g. "G`Quann", "G`Quann", "*!*@*.isp.de", ...) + * nick : nick of the one, who triggered the command + * uhost: user@host of nick + * chan : chan, where the command was triggered + * bns : + * 1 : do a botnet-seen if no matches are found + * 0 : don't do a botnet-seen + * -1 : return NULL instead of text, if no matches were found + * (necessary for botnet seen) + */ +static char *do_seen(char *mask, char *nick, char *uhost, char *chan, int bns) +{ + char hostbuf[UHOSTLEN + 1], *host, *newhost, *tmp, *dur; + seendat *l; + gseenres *r; + int wild, nr; + char bnquery[256]; + struct userrec *u; + struct laston_info *li; + struct chanset_t *ch; + + Context; + start_seentime_calc(); + if (seen_reply) { + nfree(seen_reply); + seen_reply = NULL; + } + l = NULL; + li = NULL; + host = hostbuf; + newhost = NULL; + mask = newsplit(&mask); + glob_query = mask; + while (mask[0] == ' ') + mask++; + if (!mask[0]) { + return SLNOPARAM; + } + if (strchr(mask, '?') || strchr(mask, '*')) { + // if wildcard-searches ares not allowed, then either return + // NULL (for botnet-seen), or a appropriate warning + if (!wildcard_search) { + if (bns == -1) + return NULL; + else + return SLNOWILDCARDS; + } else + wild = 1; + } else { + if (strlen(mask) > seen_nick_len) // don't process if requested nick is too long + return SLTOOLONGNICK; // (e.g. stop stupid jokes) + if (!strcasecmp(mask, nick)) { + return SLMIRROR; + } + // check if the nick is on the current channel + if (onchan(mask, chan)) + return SLONCHAN; + if ((glob_othernick = handonchan(mask, chan))) + return SLHANDONCHAN; + // check if it is on any other channel + if ((ch = onanychan(mask))) { +#if EGG_IS_MIN_VER(10500) + if (!secretchan(ch->dname)) { + glob_otherchan = ch->dname; + return SLONOTHERCHAN; + } +#else + if (!secretchan(ch->name)) { + glob_otherchan = ch->name; + return SLONOTHERCHAN; + } +#endif + } + // check if the user who uses this handle is on the channel under + // a different nick + if ((ch = handonanychan(mask))) { +#if EGG_IS_MIN_VER(10500) + if (!secretchan(ch->dname)) { + glob_otherchan = ch->dname; + return SLONOTHERCHAN; + } +#else + if (!secretchan(ch->name)) { + glob_otherchan = ch->name; + return SLONOTHERCHAN; + } +#endif + } + add_seenreq(mask, nick, uhost, chan, now); + wild = 0; + l = findseen(mask); + // if there's a result, and if we don't want to search for the same user + // under a different nick, just make a do_seennick on the result + if (l && !fuzzy_search) { + tmp = do_seennick(l); + end_seentime_calc(); + return tmp; + } + if (!l) { + u = get_user_by_handle(userlist, mask); + if (u) { + li = get_user(&USERENTRY_LASTON, u); + } + if (!u || !li) { + if (bns == -1) { // if bns is 0, then do_seen() was triggered by + end_seentime_calc(); // a botnet seen function, which needs a clear + return NULL; // NULL to detect if there was a result or not + } + tmp = SLNOTSEEN; + if (bns && ((strlen(mask) + strlen(nick) + strlen(uhost) + + strlen(chan) + 20) < 255)) { + debug0("trying botnet seen"); + if (bnsnick) + nfree(bnsnick); + if (bnschan) + nfree(bnschan); + bnsnick = nmalloc(strlen(nick) + 1); + strcpy(bnsnick, nick); + bnschan = nmalloc(strlen(chan) + 1); + strcpy(bnschan, chan); + sprintf(bnquery, "gseen_req %s %s %s %s", mask, nick, uhost, chan); + botnet_send_zapf_broad(-1, botnetnick, NULL, bnquery); + } + } else { + // we have a matching handle, no seen-entry, but a laston entry + // in the userbase, so let's just return that one. + dur = gseen_duration(now - li->laston); + glob_laston = dur; + tmp = SLPOORSEEN; + seen_reply = nmalloc(strlen(tmp) + 1); + strcpy(seen_reply, tmp); + end_seentime_calc(); + return seen_reply; + } + end_seentime_calc(); + return tmp; + } + // now prepare the host for fuzzy-search + if (strlen(l->host) < UHOSTLEN) { + maskstricthost(l->host, host); + host = strchr(host, '!') + 1; // strip nick from host for faster search + } else { + end_seentime_calc(); + return "error, too long host"; + } + } + if (l && (l->type == SEEN_CHPT)) { + tmp = do_seennick(l); + end_seentime_calc(); + return tmp; + } + numresults = 0; + // wildmatch_seens uses a global var to store hosts in it + // (to prevent massive nmalloc/nfree-usage), so don't forget + // to initialize and free it + temp_wildmatch_host = my_malloc(1); + wildmatch_seens(host, mask, wild); + my_free(temp_wildmatch_host); + temp_wildmatch_host = NULL; + if (!results) { + end_seentime_calc(); + if (bns == -1) + return NULL; // let the botnet seen function know, that seen failed + return SLNOMATCH; + } + if (numresults >= max_matches) { + end_seentime_calc(); + free_seenresults(); + return SLTOOMANYMATCHES; + } + sortresults(); + if (strcasecmp(results->seen->nick, mask)) { + // if the user's latest nick is not the nick for which we were searching, + // say that there were multiple matches and display the latest one + if (numresults == 1) + tmp = SLONEMATCH; + else if (numresults <= 5) + tmp = SLLITTLEMATCHES; + else + tmp = SLMANYMATCHES; + seen_reply = nmalloc(strlen(tmp) + 1); + strcpy(seen_reply, tmp); + nr = 0; + for (r = results; (r && (nr < 5)); r = r->next) { + nr++; + if (nr > 1) { + seen_reply = nrealloc(seen_reply, 1 + strlen(seen_reply) + 1 + strlen(r->seen->nick) + 1); + strcat(seen_reply, ", "); + } else { + seen_reply = nrealloc(seen_reply, 1 + strlen(seen_reply) + strlen(r->seen->nick) + 1); + strcat(seen_reply, " "); + } + strcat(seen_reply, r->seen->nick); + } + tmp = do_seennick(results->seen); + seen_reply = nrealloc(seen_reply, 2 + strlen(seen_reply) + strlen(tmp) + 1); + sprintf(seen_reply, "%s. %s", seen_reply, tmp); + } else { // first result is the nick which we were searching for + // just return the info for this nick and don't care about other results + tmp = do_seennick(results->seen); + seen_reply = nmalloc(strlen(tmp) + 1); + strcpy(seen_reply, tmp); + } + free_seenresults(); + end_seentime_calc(); + return seen_reply; +} + +/* do_seennick(): + * takes a seen-dataset and produces the corresponding reply basically + * by referencing to the lang entry with the same number as the seen-type. + */ +static char *do_seennick(seendat *l) +{ +// char buf[256], *msg; + int stype; + + Context; + if (!l) { + debug0("ERROR! Tryed to do a seennick on a NULL pointer!"); + return "ERROR! seendat == NULL!!!"; + } + glob_seendat = l; + // l->type is the basic language-entry-number + stype = l->type + 100; + // in some cases, we might need a special reply, so modify the + // number if neccessary + switch (l->type) { + case SEEN_JOIN: + if (!onchan(l->nick, l->chan)) + stype += 20; + break; + case SEEN_PART: + /* nothing to do here */ + break; + case SEEN_SIGN: + /* nothing again */ + break; + case SEEN_NICK: + if (!onchan(l->msg, l->chan)) + stype += 20; + break; + case SEEN_NCKF: + if (!onchan(l->nick, l->chan)) + stype += 20; + break; + case SEEN_KICK: +/* msg = buf; + strncpy(buf, l->msg, 255); + msg[255] = 0; + sglobpunisher = newsplit(&msg); + sglobreason = msg; */ + break; + case SEEN_SPLT: + /* nothing to do here */ + break; + case SEEN_REJN: + if (!onchan(l->nick, l->chan)) + stype += 20; + break; + case SEEN_CHJN: + case SEEN_CHPT: + if (!strcmp(l->chan, "0")) + stype += 20; + break; + default: + stype = 140; + } + return getslang(stype); +} + +/* findseens(): + * interface for webseen.mod + * find all results for a query and return a pointer to this list + * (basically the core of do_seen()) + */ +static gseenres *findseens(char *mask, int *ret, int fuzzy) +{ + char hostbuf[UHOSTLEN + 1], *host, *newhost; + seendat *l; + int wild; + + Context; + start_seentime_calc(); + *ret = WS_OK; + l = NULL; + host = hostbuf; + newhost = NULL; + mask = newsplit(&mask); + while (mask[0] == ' ') + mask++; + if (!mask[0]) { + *ret = WS_NOPARAM; + return NULL; + } + if (strchr(mask, '?') || strchr(mask, '*')) { + // if wildcard-searches ares not allowed, then either return + // NULL (for botnet-seen), or a appropriate warning + if (!wildcard_search) { + *ret = WS_NOWILDCARDS; + return NULL; + } + wild = 1; + } else { + if (strlen(mask) > seen_nick_len) { // don't process if requested nick is too long + *ret = WS_TOOLONGNICK; // (e.g. stop stupid jokes) + return NULL; + } + add_seenreq(mask, "www-user", "unknown_host", "webinterface", now); + wild = 0; + l = findseen(mask); + // if there's a result, and if we don't want to search for the same user + // under a different nick, just return this result + if (l && (!fuzzy_search || !fuzzy)) { + numresults = 1; + add_seenresult(l); + end_seentime_calc(); + return results; + } + if (!l) { + // no matching user was found :( + *ret = WS_NORESULT; + end_seentime_calc(); + return NULL; + } + // now prepare the host for fuzzy-search + if (strlen(l->host) < UHOSTLEN) { + maskstricthost(l->host, host); + host = strchr(host, '!') + 1; // strip nick from host for faster search + } else { + *ret = WS_TOOLONGHOST; + end_seentime_calc(); + return NULL; + } + } + if (l && (l->type == SEEN_CHPT)) { + numresults = 1; + add_seenresult(l); + end_seentime_calc(); + return results; + } + numresults = 0; + // wildmatch_seens uses a global var to store hosts in it + // (to prevent massive nmalloc/nfree-usage), so don't forget + // to initialize and free it + temp_wildmatch_host = my_malloc(1); + wildmatch_seens(host, mask, wild); + my_free(temp_wildmatch_host); + temp_wildmatch_host = NULL; + if (!results) { + // no match :( + *ret = WS_NORESULT; + end_seentime_calc(); + return NULL; + } + if (numresults >= max_matches) { + free_seenresults(); + *ret = WS_TOOMANYMATCHES; + end_seentime_calc(); + return NULL; + } + sortresults(); + *ret = 0; + end_seentime_calc(); + return results; +} + + +char seenstats_reply[512]; +static char *do_seenstats() +{ + glob_totalnicks = count_seens(); + glob_totalbytes = gseen_expmem(); + sprintf(seenstats_reply, "%s", SLSEENSTATS); + return seenstats_reply; +} + +// add an seen result (to the top of the list) +static void add_seenresult(seendat *seen) +{ + gseenres *nl; + + numresults++; + if (numresults > max_matches) + return; + nl = nmalloc(sizeof(gseenres)); + nl->seen = seen; + nl->next = results; + results = nl; +} + +static int expmem_seenresults() +{ + int bytes = 0; + gseenres *l; + + for (l = results; l; l = l->next) + bytes += sizeof(gseenres); + return bytes; +} + +static void free_seenresults() +{ + gseenres *l, *ll; + + l = results; + while (l) { + ll = l->next; + nfree(l); + l = ll; + } + results = NULL; +} + +static void sortresults() +{ + int again = 1; + gseenres *last, *p, *c, *n; + int a, b; + + Context; + again = 1; + last = NULL; + while ((results != last) && (again)) { + p = NULL; + c = results; + n = c->next; + again = 0; + while (n != last) { + if (!c || !n) + a = b = 0; + else + a = c->seen->when; + b = n->seen->when; + if (a < b) { + again = 1; + c->next = n->next; + n->next = c; + if (p == NULL) + results = n; + else + p->next = n; + } + p = c; + c = n; + n = n->next; + } + last = c; + } + Context; + return; +} + +static void sortrequests(seenreq *l) +{ + int again = 1; + seenreq_by *last, *p, *c, *n; + int a, b; + + Context; + again = 1; + last = NULL; + while ((l->by != last) && (again)) { + p = NULL; + c = l->by; + n = c->next; + again = 0; + while (n != last) { + if (!c || !n) + a = b = 0; + else + a = c->when; + b = n->when; + if (a < b) { + again = 1; + c->next = n->next; + n->next = c; + if (p == NULL) + l->by = n; + else + p->next = n; + } + p = c; + c = n; + n = n->next; + } + last = c; + } + Context; + return; +} + +/* stolen from tcl_duration in tclmisc.c */ +char gs_duration_temp[256]; +static char *gseen_duration(int seconds) +{ + char s[256]; + time_t sec; + + sec = seconds; + s[0] = 0; + if (sec < 1) { + snprintf(gs_duration_temp, sizeof(gs_duration_temp), "%s", SLSOMETIME); + return gs_duration_temp; + } + if (sec < 60) { + sprintf(gs_duration_temp, "%d %s", (int) (sec / 1), + ((int) (sec / 1) > 1) ? SLSECONDS : SLSECOND); + return gs_duration_temp; + } + if (sec >= 31536000) { + sprintf(s, "%d %s ", (int) (sec / 31536000), + ((int) (sec / 31536000) > 1) ? SLYEARS : SLYEAR); + sec -= (((int) (sec / 31536000)) * 31536000); + } + if (sec >= 604800) { + sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 604800), + ((int) (sec / 604800) > 1) ? SLWEEKS : SLWEEK); + sec -= (((int) (sec / 604800)) * 604800); + } + if (sec >= 86400) { + sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 86400), + ((int) (sec / 86400) > 1) ? SLDAYS : SLDAY); + sec -= (((int) (sec / 86400)) * 86400); + } + if (sec >= 3600) { + sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 3600), + ((int) (sec / 3600) > 1) ? SLHOURS : SLHOUR); + sec -= (((int) (sec / 3600)) * 3600); + } + if (sec >= 60) { + sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 60), + ((int) (sec / 60) > 1) ? SLMINUTES : SLMINUTE); + sec -= (((int) (sec / 60)) * 60); + } + strcpy(gs_duration_temp, s); + if (gs_duration_temp[strlen(gs_duration_temp) - 1] == ' ') + gs_duration_temp[strlen(gs_duration_temp) - 1] = 0; + return gs_duration_temp; +} + +static int onchan(char *nick, char *chan) +{ + struct chanset_t *ch; + memberlist *m; + + ch = findchan_by_dname(chan); + if (!ch) + return 0; + m = ismember(ch, nick); + if (!m) + return 0; + else if (chan_issplit(m)) + return 0; + else + return 1; +} + +/* handonchan(): + * checks if the given user is on the channel and returns its nick + */ +static char *handonchan(char *hand, char *chan) +{ + struct chanset_t *ch; + memberlist *m; + + ch = findchan_by_dname(chan); + if (!ch) + return 0; + if (ch->channel.members > 0) { + for (m = ch->channel.member; m; m = m->next) { + if (m->user) { + if (m->user->handle && !rfc_casecmp(m->user->handle, hand)) + return m->nick; + } + } + } + return NULL; +} + +/* onanychan(): + * checks if the given nickname is on any of the bot's chans. + */ +static struct chanset_t *onanychan(char *nick) +{ + struct chanset_t *ch; + memberlist *m; + + for (ch = chanset; ch; ch = ch->next) { + m = ismember(ch, nick); + if (m && !chan_issplit(m)) + return ch; + } + return NULL; +} + +/* handonanychan(): + * checks if the given user is on any channel (no matter under which nick) + */ +static struct chanset_t *handonanychan(char *hand) +{ + struct chanset_t *ch; + memberlist *m; + + for (ch = chanset; ch; ch = ch->next) { + if (ch->channel.members > 0) { + for (m = ch->channel.member; m; m = m->next) { + if (m->user) { + if (m->user->handle && !rfc_casecmp(m->user->handle, hand)) + return ch; + } + } + } + } + return NULL; +} + +static void add_seenreq(char *nick, char *from, char *host, char *chan, + time_t when) +{ + seenreq *l, *nl; + seenreq_by *b, *nb; + char buf[10] = "[secret]"; + + Context; + if (!tell_seens) + return; + if (strcmp(chan, "[partyline]") && secretchan(chan)) + chan = buf; + for (l = requests; l; l = l->next) { + if (!strcasecmp(nick, l->nick)) { + for (b = l->by; b; b = b->next) { + if (!strcasecmp(from, b->who)) { + nfree(b->chan); + b->chan = nmalloc(strlen(chan) + 1); + strcpy(b->chan, chan); + b->when = when; + return; + } + } + b = l->by; + while (b && b->next) + b = b->next; + nb = nmalloc(sizeof(seenreq_by)); + nb->who = nmalloc(strlen(from) + 1); + strcpy(nb->who, from); + nb->host = nmalloc(strlen(host) + 1); + strcpy(nb->host, host); + nb->chan = nmalloc(strlen(chan) + 1); + strcpy(nb->chan, chan); + nb->when = when; + nb->next = NULL; + if (l->by) + b->next = nb; + else + l->by = nb; + return; + } + } + nb = nmalloc(sizeof(seenreq_by)); + nb->who = nmalloc(strlen(from) + 1); + strcpy(nb->who, from); + nb->host = nmalloc(strlen(host) + 1); + strcpy(nb->host, host); + nb->chan = nmalloc(strlen(chan) + 1); + strcpy(nb->chan, chan); + nb->when = when; + nb->next = NULL; + l = requests; + while (l && l->next) + l = l->next; + nl = nmalloc(sizeof(seenreq)); + nl->nick = nmalloc(strlen(nick) + 1); + strcpy(nl->nick, nick); + nl->by = nb; + nl->next = NULL; + if (requests) + l->next = nl; + else + requests = nl; +} + +static int expmem_seenreq() +{ + seenreq *l; + seenreq_by *b; + int size; + + size = 0; + for (l = requests; l; l = l->next) { + size += sizeof(seenreq); + size += strlen(l->nick) + 1; + for (b = l->by; b; b = b->next) { + size += sizeof(seenreq_by); + size += strlen(b->who) + 1; + size += strlen(b->host) + 1; + size += strlen(b->chan) + 1; + } + } + return size; +} + +static int count_seenreq(seenreq_by *b) +{ + seenreq_by *l; + int nr; + + nr = 0; + for (l = b; l; l = l->next) + nr++; + return nr; +} + +static void free_seenreq() +{ + seenreq *l, *ll; + seenreq_by *b, *bb; + + Context; + l = requests; + while (l) { + b = l->by; + while (b) { + bb = b->next; + nfree(b->who); + nfree(b->host); + nfree(b->chan); + nfree(b); + b = bb; + } + ll = l->next; + nfree(l->nick); + nfree(l); + l = ll; + } + requests = NULL; +} + +static void report_seenreq(char *channel, char *nick) +{ + seenreq *l, *ll; + seenreq_by *b, *bb; + char *reply, *tmp; + int nr; + + if (!tell_seens) + return; + ll = NULL; + l = requests; + reply = NULL; + while (l) { + if (!strcasecmp(l->nick, nick)) { + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel)); + glob_nick = nick; + nr = count_seenreq(l->by); + if (nr == 1) { + glob_seenrequest = l; + dprintf(DP_HELP, "NOTICE %s :%s\n", l->nick, SLONELOOK); + } else { + sortrequests(l); + glob_seenrequest = l; + glob_seenrequests = nr; + tmp = SLMORELOOKS; + reply = nmalloc(strlen(tmp) + 1); + strcpy(reply, tmp); + nr = 0; + for (b = l->by; b; b = b->next) { + nr++; + reply = nrealloc(reply, strlen(reply) + ((nr == 1) ? 1 : 2) + strlen(b->who) + 1); + sprintf(reply, "%s%s%s", reply, (nr == 1) ? " " : ", ", b->who); + } + tmp = SLLASTLOOK; + reply = nrealloc(reply, strlen(reply) + 2 + strlen(tmp) + 1); + sprintf(reply, "%s. %s", reply, tmp); + dprintf(DP_HELP, "NOTICE %s :%s\n", l->nick, reply); + nfree(reply); + } + b = l->by; + while (b) { + bb = b->next; + nfree(b->who); + nfree(b->host); + nfree(b->chan); + nfree(b); + b = bb; + } + nfree(l->nick); + if (ll) + ll->next = l->next; + else + requests = l->next; + nfree(l); + if (ll) + l = ll->next; + else + l = requests; + } else { + ll = l; + l = l->next; + } + } +} + +static void start_seentime_calc() +{ + struct timeval t; + + gettimeofday(&t, NULL); + glob_presearch = (float) t.tv_sec + (((float) t.tv_usec) / 1000000); +} + +static void end_seentime_calc() +{ + struct timeval t; + + gettimeofday(&t, NULL); + glob_aftersearch = (float) t.tv_sec + (((float) t.tv_usec) / 1000000); + glob_total_searchtime += glob_aftersearch - glob_presearch; + glob_total_queries++; +} diff -Nur src/mod/gseen.mod/generic_binary_tree.c src/mod/gseen.mod/generic_binary_tree.c --- ./src/mod/gseen.mod/generic_binary_tree.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/generic_binary_tree.c 2002-10-26 13:17:51.000000000 +0200 @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define GENERIC_BINARY_TREE 1 + +struct generic_binary_tree { + void *root; + int (*comparedata) (void *data1, void *data2); + int (*expmemdata) (void *data); + void (*freedata) (void *data); +}; + +struct generic_binary_tree_node { + void *data; + void *left; + void *right; +}; + +static void btree_add(struct generic_binary_tree *, void *); +static int btree_expmem(struct generic_binary_tree *); +static int btree_recursive_expmem(struct generic_binary_tree *, struct generic_binary_tree_node *); +static void *btree_get(struct generic_binary_tree *, void *t); +static void btree_freetree(struct generic_binary_tree *); +static void btree_recursive_free(struct generic_binary_tree *, + struct generic_binary_tree_node *); +static void btree_getall(struct generic_binary_tree *, void (*) (void *)); +static void btree_recursive_getall(struct generic_binary_tree_node *, + void (*) (void *)); +static void btree_getall_expanded(struct generic_binary_tree *tree, void (*) (void *)); +static void btree_recursive_getall_expanded(struct generic_binary_tree_node *, + void (*) (void *)); +static void btree_remove(struct generic_binary_tree *, void *); + +static void btree_add(struct generic_binary_tree *tree, void *data) +{ + struct generic_binary_tree_node *node, *lastnode; + int cmp, lastcmp; + + Assert(tree); + Assert(data); + cmp = lastcmp = 0; + node = tree->root; + lastnode = NULL; + while (node) { + cmp = tree->comparedata(node->data, data); + if (!cmp) { + // item is identical -> free old data and insert new + tree->freedata(node->data); + node->data = data; + return; + } + lastnode = node; + lastcmp = cmp; + if (cmp < 0) + node = node->left; + else + node = node->right; + } + node = nmalloc(sizeof(struct generic_binary_tree_node)); + node->left = NULL; + node->right = NULL; + node->data = data; + if (!lastnode) + tree->root = node; + else { + Assert(lastcmp); + if (lastcmp < 0) { + Assert(!lastnode->left); + lastnode->left = node; + } else { + Assert(!lastnode->right); + lastnode->right = node; + } + } +} + +static int btree_expmem(struct generic_binary_tree *tree) +{ + int size = 0; + + Assert(tree); + size += btree_recursive_expmem(tree, tree->root); + return size; +} + +static int btree_recursive_expmem(struct generic_binary_tree *tree, struct generic_binary_tree_node *node) +{ + int size = 0; + + if (!node) + return 0; + size += sizeof(struct generic_binary_tree_node); + size += tree->expmemdata(node->data); + size += btree_recursive_expmem(tree, node->left); + size += btree_recursive_expmem(tree, node->right); + return size; +} + +static void *btree_get(struct generic_binary_tree *tree, void *what) +{ + struct generic_binary_tree_node *node; + int cmp; + + node = tree->root; + while (node) { + cmp = tree->comparedata(node->data, what); + if (!cmp) + return node->data; + if (cmp < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +static void btree_freetree(struct generic_binary_tree *tree) +{ + btree_recursive_free(tree, tree->root); +} + +static void btree_recursive_free(struct generic_binary_tree *tree, + struct generic_binary_tree_node *node) +{ + if (!node) + return; + btree_recursive_free(tree, node->left); + btree_recursive_free(tree, node->right); + tree->freedata(node->data); + nfree(node); +} + +/* btree_getall(): + * calls the specified function for each item in the tree. + * NOTE: getall() calls the proc _before_ it proceeds into recursion. This way, + * one can savely store the tree into a file without mixing up its form. + * But if you delete an item from the called prcedure, this function + * WILL crash. Use btree_getall() expanded instead. + */ +static void btree_getall(struct generic_binary_tree *tree, void (*func) (void *)) +{ + Assert(tree); + btree_recursive_getall(tree->root, func); +} + +static void btree_recursive_getall(struct generic_binary_tree_node *node, + void (*func) (void *)) +{ + if (!node) + return; + // first call the function, then proceed into recursion + // this way, the tree keeps in form if its saved to a file, for example + Assert(func); + func(node->data); + + btree_recursive_getall(node->left, func); + btree_recursive_getall(node->right, func); +} + +/* btree_getall_expanded(): + * the same as btree_getall(), but calls the function after the greatest level of recursion + * has been reached. The node-pointers won't be accessed anymore when the first function + * gets called. You can savely use this to free items. + */ +static void btree_getall_expanded(struct generic_binary_tree *tree, void (*func) (void *)) +{ + Assert(tree); + btree_recursive_getall_expanded(tree->root, func); +} + +static void btree_recursive_getall_expanded(struct generic_binary_tree_node *node, + void (*func) (void *)) +{ + if (!node) + return; + btree_recursive_getall_expanded(node->left, func); + btree_recursive_getall_expanded(node->right, func); + + Assert(func); + func(node->data); +} + +static void btree_remove(struct generic_binary_tree *tree, void *data) +{ + struct generic_binary_tree_node *node, *last, *largenode, *lastlarge; + int ret, lastret; + + Assert(tree); + Assert(data); + last = NULL; + lastret = 0; + node = tree->root; + while (node) { + ret = tree->comparedata(node->data, data); + if (ret == 0) + break; + last = node; + lastret = ret; + if (ret < 0) + node = node->left; + else + node = node->right; + } + if (!node) // oops, item not found + return; + if (!node->left && !node->right) { + // *freu* no sub-branches! We can easily delete this item. + if (last) { + if (lastret < 0) + last->left = NULL; + else + last->right = NULL; + } else + tree->root = NULL; + } else if (!node->left) { + // also pretty easy. Just connect the child to the parent. + if (last) { + if (lastret < 0) + last->left = node->right; + else + last->right = node->right; + } else + tree->root = node->right; + } else if (!node->right) { + // same as above, but mirrored + if (last) { + if (lastret < 0) + last->left = node->left; + else + last->right = node->left; + } else + tree->root = node->left; + } else { + // aaargh... two sub-trees! The world is not fair... *sigh* + debug0("argl... worst case, two subtrees. :( Let's pray..."); + // now we take the largest item from the left subtree and replace the + // doomed node with it. + // since it is the largest val, the tree remains valid and doesn't + // get deformed too much. + + // at first, we have to find this node and cut it from the tree + largenode = node->left; + lastlarge = NULL; + while (largenode && largenode->right) { + lastlarge = largenode; + largenode = largenode->right; + } + + // only set largenode->left to node->left if largenode exists. + // otherwise node->left points to largenode, which would result + // in a nice short-circuit + // If it does not exist, just leave largenode->left as it is because we just + // move largenode one level up, so it can keep its left subtree. + if (lastlarge) { + lastlarge->right = largenode->left; + largenode->left = node->left; + } + + // now connect node's subtrees to it + largenode->right = node->right; + + // and finally replace node with largenode + if (last) { + if (lastret < 0) + last->left = largenode; + else + last->right = largenode; + } else + tree->root = largenode; + } + // finally kill the node... we shouldn't need it anymore + tree->freedata(node->data); + nfree(node); + node = NULL; +} + +#ifdef BTREE_WITHOPTIMIZE +static void btree_optimize(struct generic_binary_tree *tree, + struct generic_binary_tree_node *node, + struct generic_binary_tree_node *last, + int limit) +{ +/* int leftdepth, rightdepth; + + if (!node) + return; + btree_optimize(tree, node->left, node, last, limit); + btree_optimize(tree, node->right, node, last, limit); + leftdepth = btree_depth(node->left); + rightdepth = btree_depth(node->right); + if ((leftdepth - rightdepth) > limit) { + + } +*/ +} +#endif diff -Nur src/mod/gseen.mod/global_vars.c src/mod/gseen.mod/global_vars.c --- ./src/mod/gseen.mod/global_vars.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/global_vars.c 2002-10-26 13:18:09.000000000 +0200 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static char *glob_query, *glob_laston, *glob_otherchan, *glob_othernick; +static char *glob_remotebot, *glob_nick; +static struct slang_header *glob_slang; +static seendat *glob_seendat; +static seenreq *glob_seenrequest; +static int glob_seenrequests, glob_totalnicks, glob_totalbytes; + +static void reset_global_vars() +{ + glob_query = glob_laston = glob_otherchan = glob_othernick = NULL; + glob_remotebot = glob_nick = NULL; + glob_seendat = NULL; + glob_slang = NULL; + glob_seenrequest = NULL; + glob_seenrequests = glob_totalnicks = glob_totalbytes = 0; +} diff -Nur src/mod/gseen.mod/gseen.c src/mod/gseen.mod/gseen.c --- ./src/mod/gseen.mod/gseen.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/gseen.c 2002-10-26 14:24:48.000000000 +0200 @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define MAKING_GSEEN +#define MODULE_NAME "gseen" +#define MODULE_VERSION "1.1.1 dev3" +#define MODULE_NUMVERSION 10100 +#include "../module.h" +#include "../irc.mod/irc.h" +#include "../server.mod/server.h" +#include "../channels.mod/channels.h" +#include +#include +#include /* for time_t */ + +#undef global +static Function *global = NULL, *irc_funcs = NULL, *server_funcs = NULL, *channels_funcs = NULL; + +#ifndef EGG_IS_MIN_VER +#define EGG_IS_MIN_VER(ver) ((ver) <= 10400) +#endif + +#ifndef EGG_IS_MAX_VER +#define EGG_IS_MAX_VER(ver) ((ver) >= 10400) +#endif + +#ifndef Context +#define Context context +#endif + +#ifndef findchan_by_dname +#define findchan_by_dname findchan +#endif + +#include "gseen.h" +#include "seenlang.h" + +static struct slang_header *coreslangs = NULL; +static gseenres *results = NULL; +static seenreq *requests = NULL; +static ignoredword *ignoredwords = NULL; +static char *bnsnick = NULL; +static char *bnschan = NULL; +static char *seen_reply = NULL; +static char *temp_wildmatch_host; +static int numresults = 0; +static double glob_presearch, glob_aftersearch; +int numseens, glob_total_queries; +double glob_total_searchtime; + +static char gseenfile[121] = "gseen.dat"; +static char no_pub[121]; +static char quiet_seen[121]; +static char quiet_ai_seen[121]; +static char no_log[121]; +static char ignore_words[1024]; +static char default_slang[21] = "eng"; +static int gseen_numversion = MODULE_NUMVERSION; +static int save_seens = 60; +static int save_seens_temp = 1; +static int expire_seens = 60; +static int maxseen_thr = 0; +static int maxseen_time = 0; +static int seenflood_thr = 0; +static time_t seenflood_time = 0; +static int use_handles = 0; +static int tell_seens = 1; +static int botnet_seen = 1; +int fuzzy_search = 1; // search for the same user under a differnt nick +static int wildcard_search = 1;// allow wildcard seaching? ("*!*@*.isp.de") +static int max_matches = 500; // break if there are more than X matches +static int hide_secret_chans = 1; // #chan (+secret) => [secret] +static int seen_nick_len = 32; + +#include "global_vars.c" +#define SLANG_NOTYPES 1 +#define SLANG_NOFACTS 1 +#define SLANG_NOGETALL 1 +#define SLANG_NOVALIDATE 1 +#include "slang.c" +#include "slang_gseen_commands.c" +#include "generic_binary_tree.c" +#include "seentree.c" +#include "datahandling.c" +#include "sensors.c" +#include "do_seen.c" +#include "gseencmds.c" +#include "ai.c" +#include "misc.c" +#include "tclcmds.c" + +static int gseen_expmem() +{ + int size = 0; + + size += seentree_expmem(); + size += expmem_seenresults(); + size += expmem_seenreq(); + size += expmem_ignoredwords(); + size += slang_expmem(coreslangs); + size += slang_glob_expmem(); + size += slang_chanlang_expmem(chanlangs); + if (bnsnick) + size += strlen(bnsnick) + 1; + if (bnschan) + size += strlen(bnschan) + 1; + if (seen_reply) { + size += strlen(seen_reply) + 1; + } + return size; +} + +static void free_gseen() +{ + seentree_free(); + slang_free(coreslangs); + slang_chanlang_free(chanlangs); + if (seen_reply) + nfree(seen_reply); + return; +} + +/* a report on the module status */ +static void gseen_report(int idx, int details) +{ + int size = 0; + + Context; + if (details) { + size = gseen_expmem(); + dprintf(idx, " using %d bytes\n", size); + } +} + +static void gseen_minutely () +{ + if (save_seens_temp >= save_seens) { + write_seens(); + save_seens_temp = 1; + } else + save_seens_temp++; +} + +static void gseen_daily () +{ + Context; + purge_seens(); +} + +static tcl_strings my_tcl_strings[] = +{ + {"gseenfile", gseenfile, 121, 0}, + {"ai-seen-ignore", ignore_words, 1024, 0}, + {"no-pub-seens", no_pub, 121, 0}, + {"quiet-seens", quiet_seen, 121, 0}, + {"quiet-ai-seens", quiet_ai_seen, 121, 0}, + {"no-log", no_log, 121, 0}, + {"no-seendata", no_log, 121, 0}, + {"default-slang", default_slang, 20, 0}, + {0, 0, 0, 0} +}; + +static tcl_ints my_tcl_ints[] = +{ + {"save-seens", &save_seens, 0}, + {"expire-seens", &expire_seens, 0}, + {"use-handles", &use_handles, 0}, + {"tell-seens", &tell_seens, 0}, + {"botnet-seens", &botnet_seen, 0}, + {"max-matches", &max_matches, 0}, + {"fuzzy-search", &fuzzy_search, 0}, + {"wildcard-search", &wildcard_search, 0}, + {"hide-secret-chans", &hide_secret_chans, 0}, + {"seen-nick-len", &seen_nick_len, 0}, + {0, 0, 0} +}; + +static tcl_coups my_tcl_coups[] = +{ + {"max-seens", &maxseen_thr, &maxseen_time}, + {0, 0, 0}, +}; + +static char *gseen_close() +{ + Context; + write_seens(); + slang_glob_free(); + free_gseen(); + free_seenreq(); + free_seenresults(); + free_ignoredwords(); + if (bnsnick) + nfree(bnsnick); + if (bnschan) + nfree(bnschan); + rem_tcl_strings(my_tcl_strings); + rem_tcl_ints(my_tcl_ints); + rem_tcl_coups(my_tcl_coups); + rem_tcl_commands(mytcls); + rem_tcl_commands(gseentcls); + rem_tcl_commands(seendebugtcls); + rem_tcl_commands(gseentcls); + rem_builtins(H_dcc, mydcc); + rem_builtins(H_join, seen_join); + rem_builtins(H_kick, seen_kick); + rem_builtins(H_nick, seen_nick); + rem_builtins(H_part, seen_part); + rem_builtins(H_sign, seen_sign); + rem_builtins(H_splt, seen_splt); + rem_builtins(H_rejn, seen_rejn); + rem_builtins(H_pub, seen_pub); + rem_builtins(H_msg, seen_msg); + rem_builtins(H_bot, seen_bot); + del_hook(HOOK_MINUTELY, (Function) gseen_minutely); + del_hook(HOOK_DAILY, (Function) gseen_daily); + module_undepend(MODULE_NAME); + return NULL; +} + +char *gseen_start(); + +static Function gseen_table[] = +{ + (Function) gseen_start, + (Function) gseen_close, + (Function) gseen_expmem, + (Function) gseen_report, + /* 4 - 7 */ + (Function) findseens, + (Function) free_seenresults, + (Function) gseen_duration, + (Function) & glob_seendat, + (Function) & numresults, + (Function) & fuzzy_search, + (Function) & numseens, + (Function) & glob_total_queries, + (Function) & glob_total_searchtime, + (Function) & gseen_numversion, +}; + +char *gseen_start(Function * global_funcs) +{ + global = global_funcs; + Context; + module_register(MODULE_NAME, gseen_table, 1, 1); + if (!(irc_funcs = module_depend(MODULE_NAME, "irc", 1, 0))) + return "You need the irc module to use the gseen module."; + if (!(server_funcs = module_depend(MODULE_NAME, "server", 1, 0))) + return "You need the server module to use the gseen module."; + if (!(channels_funcs = module_depend(MODULE_NAME, "channels", 1, 0))) + return "You need the channels module to use the gseen module."; + if (!module_depend(MODULE_NAME, "eggdrop", 107, 0)) { + if (!module_depend(MODULE_NAME, "eggdrop", 106, 0)) { + if (!module_depend(MODULE_NAME, "eggdrop", 105, 0)) { + if (!module_depend(MODULE_NAME, "eggdrop", 104, 0)) { + module_undepend(MODULE_NAME); + return "This module requires eggdrop1.4.0 or later"; + } + } + } + } + chanlangs = NULL; + coreslangs = NULL; + slang_glob_init(); + + results = NULL; + requests = NULL; + ignoredwords = NULL; + bnsnick = NULL; + bnschan = NULL; + seen_reply = NULL; + + numresults = 0; + numseens = 0; + glob_total_queries = 0; + glob_total_searchtime = 0.0; + ignore_words[0] = 0; + no_pub[0] = 0; + quiet_seen[0] = 0; + no_log[0] = 0; + seentree_init(); + add_tcl_strings(my_tcl_strings); + add_tcl_ints(my_tcl_ints); + add_tcl_coups(my_tcl_coups); + add_tcl_commands(mytcls); + add_tcl_commands(seendebugtcls); + add_tcl_commands(gseentcls); + add_builtins(H_dcc, mydcc); + add_builtins(H_join, seen_join); + add_builtins(H_kick, seen_kick); + add_builtins(H_nick, seen_nick); + add_builtins(H_part, seen_part); + add_builtins(H_sign, seen_sign); + add_builtins(H_sign, seen_sign); + add_builtins(H_splt, seen_splt); + add_builtins(H_rejn, seen_rejn); + add_builtins(H_pub, seen_pub); + add_builtins(H_msg, seen_msg); + add_builtins(H_bot, seen_bot); + read_seens(); + add_hook(HOOK_MINUTELY, (Function) gseen_minutely); + add_hook(HOOK_DAILY, (Function) gseen_daily); +#if EGG_IS_MIN_VER(10503) + initudef(1, "noseendata", 1); + initudef(1, "quietseens", 1); + initudef(1, "quietaiseens", 1); + initudef(1, "nopubseens", 1); +#endif + glob_slang_cmd_list = slang_commands_list_add(glob_slang_cmd_list, slang_text_gseen_command_table); + putlog(LOG_MISC, "*", "gseen.mod v%s loaded.", MODULE_VERSION); + return NULL; +} diff -Nur src/mod/gseen.mod/gseen.conf src/mod/gseen.mod/gseen.conf --- ./src/mod/gseen.mod/gseen.conf 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/gseen.conf 2002-10-26 13:17:54.000000000 +0200 @@ -0,0 +1,147 @@ + +###### +##### +### General Settings +##### +###### + +# the file where the seen data will be backuped. +# WARNING: set this _before_ the module is loaded. +set gseenfile "gseen.dat" + +# now load the module +loadmodule gseen + +# load the English language file +loadseenslang "en" "English" language/gseen.en.lang + +# load the German language file +loadseenslang "de" "Deutsch" language/gseen.de.lang + +# set the default language to english... +set default-slang "en" + +# ... but let #xwp use the german langfile +setchanseenlang #xwp "de" + +# the char that marks public commands (!seen, etc...) +# "" is a valid option +set cmdchar "!" + +# delete data sets that are older than x days +set expire-seens 60 + +# only answer x seen requests in y seconds to prevent flooding +set max-seens 7:60 + +# tell users if someone was !seen'ing for them +set tell-seens 1 + +# check if the user was online under a different nick +set fuzzy-search 1 + +# allow user to include wildcards in the search? +set wildcard-search 1 + +# break search if there are more than x matches +set max-matches 250 + +# forward a request to other bots, if a !seen returned no result? +set botnet-seens 1 + +# store channels, which are +secret on the bot as [secret]? +set hide-secret-chans 1 + +# backup the seen data every x minutes +set save-seens 60 + +###### +##### +### AI Settings +##### +###### + +# this setting configures on which sentences your bot should +# attempt to do an ai-seen. Each of them is a simple wildcard +# mask. Set this to "" if you want to deactivate ai-seens or +# create more precise masks if the bots reacts too often. +set ai-seen-binds { + "${nick}*seen*" + "${botnet-nick}*seen*" + "${nick}*gesehen*" + "${botnet-nick}*gesehen*" +} + +# this is just the same as above, but if triggered it will +# not do an ai-seen, but display its seen-stats. +set ai-seenstats-binds { + "${nick}*seenstats*" + "${botnet-nick}*seenstats*" +} + +# when doing an AI seen, ignore the following words (otherwise +# the bot might give weird answers like " nick, bot was last seen..." :) +set ai-seen-ignore "$nick ${botnet-nick} seen" + +###### +##### +### special stuff (can be ignored in most cases) +##### +###### + +# if the user is known by the bot, log their handle instead of the nick +# (not recommended, might cause confusion by the users) +set use-handles 0 + +###### +##### +### outdated settings (only important for eggdropv1.4 users) +##### +###### + +# channels where you do not want your bot to reply to public queries +set no-pub-seens "" + +# channels where you want your bot to send replies via notice to the user and +# not to the channel +set quiet-seens "" + +# same as quiet-seens but for AI seen +set quiet-ai-seens "" + +# channels where you do not want your bot to log seen data +set no-seendata "" + + +############################################################################### +# end of configuration +# just ignore everything below ^_^ +############################################################################### + +bind chjn - * *chjn:gseen +bind chpt - * *chpt:gseen + +catch "unbind pub - !seen *pub:!seen" +catch "unbind pub - !seennick *pub:!seennick" +catch "unbind pub - !seenstats *pub:!seenstats" +bind pub - ${cmdchar}seen *pub:!seen +bind pub - ${cmdchar}seennick *pub:!seennick +bind pub - ${cmdchar}seenstats *pub:!seenstats + +foreach bnd [binds pubm] { + if {([lindex $bnd 2] == "*pubm:seen") || ([lindex $bnd 2] == "*pub:!seenstats")} { + unbind [lindex $bnd 0] [lindex $bnd 1] [lindex $bnd 2] [lindex $bnd 4] + } +} + +if {${ai-seen-binds} != ""} { + foreach mask ${ai-seen-binds} { + bind pubm -|- "% [subst $mask]" *pubm:seen + } +} + +if {${ai-seenstats-binds} != ""} { + foreach mask ${ai-seenstats-binds} { + bind pubm -|- "% [subst $mask]" *pub:!seenstats + } +} diff -Nur src/mod/gseen.mod/language/gseen.de.lang src/mod/gseen.mod/language/gseen.de.lang --- ./src/mod/gseen.mod/language/gseen.de.lang 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/language/gseen.de.lang 2002-10-26 13:18:12.000000000 +0200 @@ -0,0 +1,131 @@ +##################################################################### +# +# Deutsche Sprachdatei für GSeen.Mod v1.1.0 +# +# Der Text in dieser Datei kann nach belieben verändert werden. Du +# kannst Tags hinzufügen oder entfernen, wie es Dir gefällt. Die Tags +# müssen nicht in einer bestimmten Reihenfolge oder Anzahl vorkommen. +# +# Wenn Du mehr als eine Zeile pro ID angibst, dann wird bei der +# Antwort per Zufall eine daraus ausgewählt. (das funktioniert nicht +# bei den Zeiteinheiten) +# +# Falls Du denkst, daß hier noch ein paar wichtige Tags fehlen, dann +# schick mir einfach eine email. Vielleicht füge ich sie dann in der +# nächsten Version hinzu. +# +# Eine komplette Liste der Verfügbaren Tags befindet sich am Ende von +# slang_gseen_commands.c (leider ohne Erklährungen) +# +##################################################################### + +# +## Zeiteinheiten +# +# jeweils in Singular und Plural +# +D 0 Jahr +D 1 Jahre +D 2 Woche +D 3 Wochen +D 4 Tag +D 5 Tage +D 6 Stunde +D 7 Stunden +D 8 Minute +D 9 Minuten +D 10 Sekunde +D 11 Sekunden +# falls ein üngültiger Zeitwert angegeben war, dann wird dieser Text ausgegeben: +D 12 einiger Zeit + + +# +## Präfixe +# +# Dieses Fragment wird jeweils vor eine Antwort gesetzt. Dadurch +# ist beispielsweise bei öffentlichen Anfragen ersichtlich, für +# wen die Antwort ist. +# Achtung: Die Nummer muss auf jeden Fall definiert werden. Sie muss +# zwar keinen Text beinhalten, aber wenn sie nicht vorhanden +# ist, dann gibt es eine Fehlermeldung + +# für Antworten, die in den Channel geschrieben werden: +10 , +# für Antworten, die per NOTICE an den User geschickt werden: +11 +# für Antworten auf Anfragen, die per "/msg seen" erfolgt sind: +12 +# und für Antworten auf der Partyline: +13 + +# +## Fehlermeldungen +# +54 weißt Du was ein Parameter ist? ^_^ +54 ich würde Dir ja gerne helfen, aber solange Du nicht sagst, nach wem Du suchst, kann ich nicht viel tun. +54 meinst Du nicht, es wäre geschickter zu sagen, nach wem Du überhaupt suchst? +54 42. +55 sehe ich etwa wie ein Spiegel aus? ^_^ +55 Spieglein, Spieglein an der Wand... +55 leidest Du etwa unter multiplen Persönlichkeiten? *eg* +56 also wenn Du jetzt hier nicht sehen kannst, dann brauchst Du sicherlich eine neue Brille ^_^ +56 ich muss mir unbedingt mal die Tarnkappe von ausleihen. Scheint ja prima zu funktioneren. +56 schau Dir bitte nochmal ganz genau an, wer grade alles im Channel ist. +57 Tut mir leid, aber Wildcards ('?', oder '*') sind bei der Suche nicht erlaubt. +58 Öhm... naja... etwas arg lang, dieser Nick... :) + +# +## Kein Ergebnis +# +65 Ich kann mich nicht daran erinnern, gesehen zu haben... +65 ? Hmm... ich bin mir nicht sicher... vielleicht... eventuell... nein, kenne ich nicht. +65 der Name sagt mir nichts. Hast Du Dich vielleicht vertippt? +66 Ich hab' seit nicht mehr gesehen. +67 Sorry, aber zu deiner Anfrage passt nichts in meiner Datenbank :( +68 Autschi, das gab viel zu viele Ergebnisse. Bitte formuliere deine Suche etwas genauer. + +73 ist grade unter dem Nick "" in diesem Channel zu finden. +74 ist gerade in . +75 Deine Anfrage führte zu genau einem Ergebnis: +76 Immerhin Treffer ergab deine Anfrage: +77 Wow, auf deine Anfrage passen sogar Einträge in meiner Datenbank! Dies sind die 5 aktuellsten: + +# +## falls ein anderer Bot etwas gefunden hat: +# +85 sagt: + +# +## die eigentliche Information +# +101 Ich habe () zuletzt vor betreten sehen (). ist noch immer da. +121 Ich habe () zuletzt vor betreten sehen (), aber verschwand mysteriöserweise. +102 Ich habe () zuletzt vor nach verchatteter Zeit verlassen sehen () +103 Ich habe () zuletzt in gesehen, als er/sie vor () nach das IRC verließ (""). +104 Zuletzt habe ich () vor in gesehen, den Nick zu wechselnd. ist noch immer dort. +124 () was last seen changing his/her nick to on ago (), but mysteriously dematerialized. +105 Zuletzt habe ich () vor in gesehen, den Nick von wechselnd. ist noch immer dort. +125 () was last seen changing his/her nick from on ago (), but mysteriously dematerialized. +106 Zuletzt habe ich () gesehen, als er vor () von aus gejagt wurde. () +107 () habe ich zuletzt vor gesehen, als er/sie von aus in einem Netsplit verschwand. +108 () habe ich zuletzt vor gesehen, als er/sie nach einem Netsplit in zurück kam. ist noch immer dort. +128 () habe ich zuletzt vor gesehen, als er/sie nach einem Netsplit in zurück kam. Allerdings konnte dem Gott der Netsplits nicht endgültig entkommen und ist wieder verschollen... +109 was last seen joining the botnet channel on ago (). +129 was last seen joining the partyline on ago (). +110 was last seen leaving the botnet channel from ago (). +130 was last seen leaving the partyline from ago (). +140 () was last seen on ago (). + +# +## Seen-Mitteilungen +# +170 () scheint vor () in auf der Suche nach Dir gewesen zu sein. +171 Leute haben sich nach Dir erkundigt: +172 Der/die letzte war () in vor (). + +# +## Statistiken +# +180 Momentan sind Nicks in meiner Datenbank. Gesamter Speicherverbrauch: Bytes +180 In meiner Datenbank befinden sich Nicks und verbrauchen Bytes Speicher. diff -Nur src/mod/gseen.mod/language/gseen.en.lang src/mod/gseen.mod/language/gseen.en.lang --- ./src/mod/gseen.mod/language/gseen.en.lang 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/language/gseen.en.lang 2002-10-26 13:18:13.000000000 +0200 @@ -0,0 +1,131 @@ +##################################################################### +# +# Default English langfile for GSeen.Mod v1.1.0 +# +# Just edit the text below to fit your needs. You can add or remove +# any tag just like you want, they do not need to appear in a special +# order (or number). +# +# If you enter more than one line per ID, then a random one will be +# chosen for each reply. (this does not work for the time strings) +# +# If you think you need more tags, just email me and maybe I'll add +# them in the next release. +# +# A complete list of available Tags can be found at the end of the +# file slang_gseen_commands.c (unfortunately, it does not contain any +# descriptions for the tags) +# +##################################################################### + +# +## time string +# +# each time string in singular and plural +# +D 0 year +D 1 years +D 2 week +D 3 weeks +D 4 day +D 5 days +D 6 hour +D 7 hours +D 8 minute +D 9 minutes +D 10 second +D 11 seconds +# if an invalid time value was supplied, output the following string +D 12 some time + +# +## Prefixes +# +# These are the prefixes of the replies. By default, there's only +# a prefix for public requests (so you know for whom the answer is), +# but you can also define prefixes for other requests. + +# for replies in the channel: +10 , +# for replies via notice: +11 +# for replies via PRIVMSG +12 +# for replies on the partyline +13 + +# +## error messages +# +54 do you know what a parameter is? +54 don't you think it would be more reasonable to say for whom you are searching? +54 42. +55 do I look like a mirror? ^_^ +55 mirror mirror on the wall... +55 do you have a split personality? *eg* +56 if you can't see here right now, you probably need new glasses. ^_^ +56 please look a bit closer at the memberlist of this channel. +57 I'm sorry, but wildcards ('?' or '*') are not allowed in a search. +58 Hum... don't you think this nick is a bit long? ^_^ +58 you know that the length of nicks is limited, don't you? + +# +## no result +# +65 I don't remember seeing . +65 ? hmm... I'm trying to remember... maybe... I'm not sure... no. I don't remember . +66 I haven't seen for . +67 I found no matches to your query. +67 I'm sorry, but your search didn't return any results. +68 Ouch, your search returned way too many matches. Please refine it. + +# +## victim is online +# +73 is , who is on this channel right now. +74 is on right now. + +# +## results found +# +75 I found one match to your query: +76 I found matches to your query: +77 I found matches to your query. These are the 5 most recent ones: + +# +## results found by another bot in the botnet +# +85 says: + +# +## the core info +# +101 () was last seen joining ago (). is still there. +121 () was last seen joining ago (), but mysteriously dematerialized. +102 () was last seen parting ago (), after spending there. +103 () was last seen quitting ago () stating "" after spending there. +104 () was last seen changing his/her nick to on ago (). is still there. +124 () was last seen changing his/her nick to on ago (), but mysteriously dematerialized. +105 () was last seen changing his/her nick from on ago (). is still there. +125 () was last seen changing his/her nick from on ago (), but mysteriously dematerialized. +106 () was last seen being kicked from by () ago (), after spending there. +107 () was last seen splitting from ago (), after spending there. +108 () was last seen rejoining from a netsplit ago () is still there. +128 () was last seen rejoining from a netsplit ago (), but the god of netsplits didn't let him escape, so he's not here now. +109 was last seen joining the botnet channel on ago (). +129 was last seen joining the partyline on ago (). +110 was last seen leaving the botnet channel from ago (). +130 was last seen leaving the partyline from ago (). +140 () was last seen on ago (). + +# +## seen notification +# +170 () was looking for you on ago (). +171 There have been users looking for you: +172 The last one was () on ago (). + +# +## seen stats +# +180 I'm currently tracking nicks using bytes. diff -Nur src/mod/gseen.mod/gseen.h src/mod/gseen.mod/gseen.h --- ./src/mod/gseen.mod/gseen.h 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/gseen.h 2002-10-26 13:17:55.000000000 +0200 @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* #define USE_MEMDEBUG 1 */ + +#define SEEN_JOIN 1 +#define SEEN_PART 2 +#define SEEN_SIGN 3 +#define SEEN_NICK 4 +#define SEEN_NCKF 5 +#define SEEN_KICK 6 +#define SEEN_SPLT 7 +#define SEEN_REJN 8 +#define SEEN_CHPT 9 +#define SEEN_CHJN 10 + +typedef struct gseen_data { + int type; + char *nick; + char *host; + char *chan; + char *msg; + time_t when; + int spent; +} seendat; + +typedef struct gseen_result { + struct gseen_result *next; + seendat *seen; +} gseenres; + +typedef struct gseen_requests { + struct gseen_requests *next; + char *who; + char *host; + char *chan; + time_t when; +} seenreq_by; + +typedef struct gseen_request { + struct gseen_request *next; + char *nick; + struct gseen_requests *by; +} seenreq; + +typedef struct gseen_ignorewords { + struct gseen_ignorewords *next; + char *word; +} ignoredword; + +#ifdef MAKING_GSEEN +static int gseen_expmem(); +static void free_gseen(); +static int get_spent(char *, char *); +static void write_seens(); +static void read_seens(); +static char *do_seen(char *, char *, char *, char *, int); +static void add_seenresult(seendat *); +static int expmem_seenresults(); +static void free_seenresults(); +static void sortresults(); +static char *do_seennick(seendat *); +static int onchan(char *, char *); +static char *handonchan(char *, char *); +static struct chanset_t *onanychan(char *); +static struct chanset_t *handonanychan(char *); +static char *do_seenstats(); +static void add_seenreq(char *, char *, char *, char *, time_t); +static int expmem_seenreq(); +static void free_seenreq(); +static void sortrequests(seenreq *); +static void report_seenreq(char *, char *); +static int count_seenreq(seenreq_by *b); +static int expmem_ignoredwords(); +static void free_ignoredwords(); +static void add_ignoredword(char *word); +static int word_is_ignored(char *word); +static void purge_seens(); +static int seenflood(); +static int secretchan(char *); +static int nopub(char *); +static int quietseen(char *); +static int quietaiseens(char *); +static int nolog(char *); +static void start_seentime_calc(); +static void end_seentime_calc(); +#endif + + +#ifdef MAKING_GSEEN + +// tree stuff +static void maskstricthost(const char *, char *); +#endif + +// interface for webseen +#define WS_OK 0 +#define WS_NORESULT 1 +#define WS_NOPARAM 2 +#define WS_NOWILDCARDS 3 +#define WS_TOOLONGNICK 4 +#define WS_TOOMANYMATCHES 5 +#define WS_TOOLONGHOST 6 + +#ifndef MAKING_GSEEN +#define findseens ((gseenres *(*)(char *, int *, int))gseen_funcs[4]) +#define free_seenresults ((void (*)())gseen_funcs[5]) +#define gseen_duration ((char *(*)(int))gseen_funcs[6]) +#define numresults (*(int *)(gseen_funcs[12])) +#define fuzzy_search (*(int *)(gseen_funcs[13])) +#define numseens (*(int *)(gseen_funcs[15])) +#define glob_total_queries (*(int *)(gseen_funcs[16])) +#define glob_total_searchtime (*(double *)(gseen_funcs[17])) +#define gseen_numversion (*(int *)(gseen_funcs[19])) +#else +static gseenres *findseens(char *, int *, int); +static char *gseen_duration(int); +#endif + +#ifdef MAKING_GSEEN + +#ifdef malloc +#undef malloc +#endif +#ifdef free +#undef free +#endif +#ifdef realloc +#undef realloc +#endif + +#ifdef USE_MEMDEBUG +#define my_malloc nmalloc +#define my_free nfree +#define my_realloc nrealloc +#else +#define my_malloc malloc +#define my_free free +#define my_realloc realloc +#endif + +#endif diff -Nur src/mod/gseen.mod/gseencmds.c src/mod/gseen.mod/gseencmds.c --- ./src/mod/gseen.mod/gseencmds.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/gseencmds.c 2002-10-26 13:17:56.000000000 +0200 @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define PREFIX_LENGTH 20 + +static char reply_prefix[PREFIX_LENGTH + 1]; +#define set_prefix(x) strncpy(reply_prefix, x, PREFIX_LENGTH); \ + reply_prefix[PREFIX_LENGTH] = 0; + +static int seenflood() +{ + if (!maxseen_thr || !maxseen_time) + return 0; + if ((now - seenflood_time) > maxseen_time) { + seenflood_time = now; + seenflood_thr = 0; + } + seenflood_thr++; + if (seenflood_thr > maxseen_thr) + return 1; + else + return 0; +} + +static int nopub(char *chan) +{ + char buf[121], *b; + + Context; + strncpy(buf, no_pub, 120); + buf[120] = 0; + b = buf; + while (b[0]) + if (!strcasecmp(chan, newsplit(&b))) + return 1; +#if EGG_IS_MIN_VER(10503) + if (ngetudef("nopubseens", chan)) + return 1; +#endif + return 0; +} + +static int quietseen(char *chan) +{ + char buf[121], *b; + + Context; + strncpy(buf, quiet_seen, 120); + buf[120] = 0; + b = buf; + while (b[0]) + if (!strcasecmp(chan, newsplit(&b))) + return 1; +#if EGG_IS_MIN_VER(10503) + if (ngetudef("quietseens", chan)) + return 1; +#endif + return 0; +} + +static int cmd_seen(struct userrec *u, int idx, char *par) +{ + char *query; + + Context; + if (seenflood()) + return 0; + reset_global_vars(); + glob_slang = slang_find(coreslangs, default_slang); + glob_nick = dcc[idx].nick; + query = newsplit(&par); + glob_query = query; + set_prefix(SLDCCPREFIX); + putlog(LOG_CMDS, "*", "#%s# seen %s", dcc[idx].nick, par); + dprintf(idx, "%s%s\n", reply_prefix, do_seen(query, dcc[idx].nick, + dcc[idx].host, "[partyline]", botnet_seen)); + return 0; +} + +static int cmd_seenstats(struct userrec *u, int idx, char *par) +{ + Context; + if (seenflood()) + return 0; + reset_global_vars(); + glob_slang = slang_find(coreslangs, default_slang); + glob_nick = dcc[idx].nick; + set_prefix(SLDCCPREFIX); + putlog(LOG_CMDS, "*", "#%s# seenstats", dcc[idx].nick); + dprintf(idx, "%s%s\n", reply_prefix, do_seenstats()); + return 0; +} + +static int cmd_purgeseens(struct userrec *u, int idx, char *par) +{ + Context; + purge_seens(); + putlog(LOG_CMDS, "*", "#%s# purgeseens", dcc[idx].nick); + return 0; +} + +static int pub_seen(char *nick, char *host, char *hand, + char *channel, char *text) +{ + char *dest; +#if EGG_IS_MIN_VER(10500) + struct chanset_t *chan; +#endif + + Context; + if (seenflood() || nopub(channel)) + return 0; + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel)); + glob_nick = nick; + putlog(LOG_CMDS, "*", "<<%s>> !%s! seen %s", nick, hand, text); + if (quietseen(channel)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, + do_seen(newsplit(&text), nick, host, channel, botnet_seen)); + return 0; + } +#if EGG_IS_MIN_VER(10500) + chan = findchan_by_dname(channel); + if (chan) + dest = chan->name; + else + dest = channel; +#else + dest = channel; +#endif + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, + do_seen(newsplit(&text), nick, host, channel, botnet_seen)); + return 0; +} + +static int pub_seenstats(char *nick, char *host, char *hand, + char *channel, char *text) +{ + char *dest; +#if EGG_IS_MIN_VER(10500) + struct chanset_t *chan; +#endif + + Context; + if (seenflood()) + return 0; + if (nopub(channel)) + return 0; + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel)); + glob_nick = nick; + putlog(LOG_CMDS, "*", "<<%s>> !%s! seenstats", nick, hand); + if (quietseen(channel)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, do_seenstats()); + return 0; + } +#if EGG_IS_MIN_VER(10500) + chan = findchan_by_dname(channel); + if (chan) + dest = chan->name; + else + dest = channel; +#else + dest = channel; +#endif + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, do_seenstats()); + return 1; +} + +static int msg_seen(char *nick, char *uhost, struct userrec *u, char *text) +{ + Context; + if (seenflood()) + return 0; + reset_global_vars(); + glob_slang = slang_getbynick(coreslangs, nick); + glob_nick = nick; + putlog(LOG_CMDS, "*", "(%s!%s) !%s! seen %s", nick, uhost, u ? u->handle : "*", text); + set_prefix(SLMSGPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix, + do_seen(newsplit(&text), nick, uhost, "[/msg]", botnet_seen)); + return 1; +} + +static int pub_seennick(char *nick, char *host, char *hand, + char *channel, char *text) +{ + seendat *l; + char *dest; +#if EGG_IS_MIN_VER(10500) + struct chanset_t *chan; +#endif + + Context; + if (seenflood()) + return 0; + if (nopub(channel)) + return 0; + putlog(LOG_CMDS, "*", "<<%s>> !%s! seennick %s", nick, hand, text); + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel)); + glob_nick = nick; +#if EGG_IS_MIN_VER(10500) + chan = findchan_by_dname(channel); + if (chan) + dest = chan->name; + else + dest = channel; +#else + dest = channel; +#endif + text = newsplit(&text); + l = findseen(text); + if (!l) { + glob_query = text; + if (quietseen(channel)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, SLNOTSEEN); + } else { + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, SLNOTSEEN); + } + return 0; + } + if (quietseen(channel)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, do_seennick(l)); + } else { + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, do_seennick(l)); + } + return 0; +} + +static int msg_seennick(char *nick, char *uhost, struct userrec *u, char *text) +{ + seendat *l; + + Context; + if (seenflood()) + return 0; + putlog(LOG_CMDS, "*", "(%s!%s) !%s! seennick %s", nick, uhost, u ? u->handle : "*", text); + reset_global_vars(); + glob_slang = slang_getbynick(coreslangs, nick); + glob_nick = nick; + set_prefix(SLMSGPREFIX); + text = newsplit(&text); + l = findseen(text); + if (!l) { + glob_query = text; + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix, SLNOTSEEN); + return 0; + } + dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix, do_seennick(l)); + return 0; +} + +static int cmd_seennick(struct userrec *u, int idx, char *text) +{ + seendat *l; + + Context; + if (seenflood()) + return 0; + putlog(LOG_CMDS, "*", "#%s# seennick %s", dcc[idx].nick, text); + reset_global_vars(); + glob_slang = slang_find(coreslangs, default_slang); + glob_nick = dcc[idx].nick; + set_prefix(SLMSGPREFIX); + text = newsplit(&text); + l = findseen(text); + if (!l) { + glob_query = text; + dprintf(idx, "%s%s\n", reply_prefix, SLNOTSEEN); + return 0; + } + dprintf(idx, "%s%s\n", reply_prefix, do_seennick(l)); + return 0; +} + +static int bot_gseen_req(char *bot, char *code, char *par) +{ + char *mask, *nick, *uhost, *chan, *reply; + char tosend[256]; + int i; + + Context; + if (seenflood()) + return 0; + i = nextbot(bot); + if (i < 0) { + debug1("Couldn't answer botnet-seen-request from %s: no such bot", bot); + return 0; + } + mask = newsplit(&par); + nick = newsplit(&par); + uhost = newsplit(&par); + chan = newsplit(&par); + reset_global_vars(); + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan)); + glob_nick = nick; + reply = do_seen(mask, nick, uhost, chan, -1); + if (!reply) + return 0; + if ((strlen(nick) + strlen(chan) + strlen(reply)) < 255) { + sprintf(tosend, "gseen_rep %s %s %s", nick, chan, reply); + botnet_send_zapf(i, botnetnick, bot, tosend); + } + return 0; +} + +static int bot_gseen_rep(char *bot, char *code, char *par) +{ + char *nick, *chan, *reply; + int i; + + Context; + if (seenflood()) + return 0; + if (!bnsnick || !bnschan) { + if (bnsnick) + nfree(bnsnick); + if (bnschan) + nfree(bnschan); + bnsnick = bnschan = NULL; + return 0; + } + nick = newsplit(&par); + chan = newsplit(&par); + reset_global_vars(); + glob_remotebot = bot; + glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan)); + glob_nick = nick; + reply = par; + if (strcmp(nick, bnsnick) || strcmp(chan, bnschan)) + return 0; /* unwanted reply */ + if (findchan(chan)) { + if (nopub(chan)) { + nfree(bnsnick); + nfree(bnschan); + bnsnick = bnschan = NULL; + debug1("%s is nopub, bns-reply dropped", chan); + return 0; + } + if (quietseen(chan)) { + set_prefix(SLNOTPREFIX); + dprintf(DP_HELP, "NOTICE %s :%s%s%s\n", nick, reply_prefix, SLRBOTSAYS, reply); + } else { + set_prefix(SLPUBPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s%s\n", chan, reply_prefix, SLRBOTSAYS, reply); + } + } else if (!strcmp(chan, "[/msg]")) { + set_prefix(SLMSGPREFIX); + dprintf(DP_HELP, "PRIVMSG %s :%s%s%s\n", nick, reply_prefix, SLRBOTSAYS, reply); + } else if (!strcmp(chan, "[partyline]")) { + for (i = 0; i < dcc_total; i++) { + if ((!strcasecmp(nick, dcc[i].nick)) && + (dcc[i].type->flags & DCT_SIMUL)) { + set_prefix(SLDCCPREFIX); + dprintf(i, "%s%s%s\n", reply_prefix, SLRBOTSAYS, reply); + break; + } + } + } else + debug1("Couldn't send received bns answer, no such chan %s", chan); + nfree(bnsnick); + nfree(bnschan); + bnsnick = bnschan = NULL; + return 0; +} + +static cmd_t mydcc[] = +{ + {"seen", "-|-", cmd_seen, NULL}, + {"seenstats", "-|-", cmd_seenstats, NULL}, + {"purgeseens", "m", cmd_purgeseens, NULL}, + {"seennick", "-|-", cmd_seennick, NULL}, + {0, 0, 0, 0} +}; + +static cmd_t seen_pub[] = +{ + {"!seen", "", pub_seen, 0}, + {"!seenstats", "", pub_seenstats, 0}, + {"!seennick", "", pub_seennick, 0}, + {0, 0, 0, 0} +}; + +static cmd_t seen_msg[] = +{ + {"seen", "", msg_seen, 0}, + {"seennick", "", msg_seennick, 0}, + {0, 0, 0, 0} +}; + +static cmd_t seen_bot[] = +{ + {"gseen_req", "", bot_gseen_req, 0}, + {"gseen_rep", "", bot_gseen_rep, 0}, + {0, 0, 0, 0} +}; diff -Nur src/mod/gseen.mod/misc.c src/mod/gseen.mod/misc.c --- ./src/mod/gseen.mod/misc.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/misc.c 2002-10-26 13:17:57.000000000 +0200 @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* maskstricthost(): + * basically the same as maskhost() from src/misc.c, but _never_ stripts + * "~+-^=" off the host + * maskhost() version: * $Id: misc.c,v 1.30 2000/10/27 19:27:32 fabian Exp $ + */ +static void maskstricthost(const char *s, char *nw) +{ + register const char *p, *q, *e, *f; + int i; + + *nw++ = '*'; + *nw++ = '!'; + p = (q = strchr(s, '!')) ? q + 1 : s; + /* Strip of any nick, if a username is found, use last 8 chars */ + if ((q = strchr(p, '@'))) { + int fl = 0; + + if ((q - p) > 9) { + nw[0] = '*'; + p = q - 7; + i = 1; + } else + i = 0; + while (*p != '@') { + if (!fl && strchr("~+-^=", *p)) { +// if (strict_host) + nw[i] = '?'; +// else +// i--; + } else + nw[i] = *p; + fl++; + p++; + i++; + } + nw[i++] = '@'; + q++; + } else { + nw[0] = '*'; + nw[1] = '@'; + i = 2; + q = s; + } + nw += i; + e = NULL; + /* Now q points to the hostname, i point to where to put the mask */ + if ((!(p = strchr(q, '.')) || !(e = strchr(p + 1, '.'))) && !strchr(q, ':')) + /* TLD or 2 part host */ + strcpy(nw, q); + else { + if (e == NULL) { /* IPv6 address? */ + const char *mask_str; + + f = strrchr(q, ':'); + if (strchr(f, '.')) { /* IPv4 wrapped in an IPv6? */ + f = strrchr(f, '.'); + mask_str = ".*"; + } else /* ... no, true IPv6. */ + mask_str = ":*"; + strncpy(nw, q, f - q); + /* No need to nw[f-q] = 0 here, as the strcpy below will + * terminate the string for us. + */ + nw += (f - q); + strcpy(nw, mask_str); + } else { + for (f = e; *f; f++); + f--; + if (*f >= '0' && *f <= '9') { /* Numeric IP address */ + while (*f != '.') + f--; + strncpy(nw, q, f - q); + /* No need to nw[f-q] = 0 here, as the strcpy below will + * terminate the string for us. + */ + nw += (f - q); + strcpy(nw, ".*"); + } else { /* Normal host >= 3 parts */ + /* a.b.c -> *.b.c + * a.b.c.d -> *.b.c.d if tld is a country (2 chars) + * OR *.c.d if tld is com/edu/etc (3 chars) + * a.b.c.d.e -> *.c.d.e etc + */ + const char *x = strchr(e + 1, '.'); + + if (!x) + x = p; + else if (strchr(x + 1, '.')) + x = e; + else if (strlen(x) == 3) + x = p; + else + x = e; + sprintf(nw, "*%s", x); + } + } + } +} diff -Nur src/mod/gseen.mod/seenlang.h src/mod/gseen.mod/seenlang.h --- ./src/mod/gseen.mod/seenlang.h 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/seenlang.h 2002-10-26 13:17:58.000000000 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define SLPUBPREFIX getslang(10) +#define SLNOTPREFIX getslang(11) +#define SLMSGPREFIX getslang(12) +#define SLDCCPREFIX getslang(13) + +#define SLNOPARAM getslang(54) +#define SLMIRROR getslang(55) +#define SLONCHAN getslang(56) +#define SLNOWILDCARDS getslang(57) +#define SLTOOLONGNICK getslang(58) + +#define SLNOTSEEN getslang(65) +#define SLPOORSEEN getslang(66) +#define SLNOMATCH getslang(67) +#define SLTOOMANYMATCHES getslang(68) + +#define SLHANDONCHAN getslang(73) +#define SLONOTHERCHAN getslang(74) +#define SLONEMATCH getslang(75) +#define SLLITTLEMATCHES getslang(76) +#define SLMANYMATCHES getslang(77) + +#define SLRBOTSAYS getslang(85) + +#define SLYEAR getdur(0) +#define SLYEARS getdur(1) +#define SLWEEK getdur(2) +#define SLWEEKS getdur(3) +#define SLDAY getdur(4) +#define SLDAYS getdur(5) +#define SLHOUR getdur(6) +#define SLHOURS getdur(7) +#define SLMINUTE getdur(8) +#define SLMINUTES getdur(9) +#define SLSECOND getdur(10) +#define SLSECONDS getdur(11) +#define SLSOMETIME getdur(12) + +#define SLONELOOK getslang(170) +#define SLMORELOOKS getslang(171) +#define SLLASTLOOK getslang(172) + +#define SLSEENSTATS getslang(180) diff -Nur src/mod/gseen.mod/seentree.c src/mod/gseen.mod/seentree.c --- ./src/mod/gseen.mod/seentree.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/seentree.c 2002-10-26 13:18:10.000000000 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static struct generic_binary_tree seentree; + +static void seentree_init(); +static int seentree_expmem(); +static void seentree_free(); +static int compareseens(void *, void *); +static int expmemseen(void *); +static void add_seen(int, char *, char *, char *, char *, + time_t, int); +static void freeseen(void *); +static seendat *findseen(char *); +static void wildmatch_seens(char *, char *, int); +static void process_wildmatch_seens(void *); +static void write_seen_tree(void *); +static void purge_seen_tree(void *); +static int count_seens(); +static void _count_seens(void *); + + +static void seentree_init() +{ + seentree.root = NULL; + seentree.comparedata = compareseens; + seentree.expmemdata = expmemseen; + seentree.freedata = freeseen; +} + +static int seentree_expmem() +{ + return btree_expmem(&seentree); +} + +static void seentree_free() +{ + btree_freetree(&seentree); + seentree.root = NULL; +} + +static int compareseens(void *first, void *second) +{ + return rfc_casecmp(((seendat *) first)->nick, ((seendat *) second)->nick); +} + +// add another entry to the tree +static void add_seen(int type, char *nick, char *host, char *chan, char *msg, + time_t when, int spent) +{ + seendat *newseen; + + newseen = nmalloc(sizeof(seendat)); + newseen->type = type; + newseen->nick = nmalloc(strlen(nick) + 1); + strcpy(newseen->nick, nick); + newseen->host = nmalloc(strlen(host) + 1); + strcpy(newseen->host, host); + newseen->chan = nmalloc(strlen(chan) + 1); + strcpy(newseen->chan, chan); + newseen->msg = nmalloc(strlen(msg) + 1); + strcpy(newseen->msg, msg); + newseen->when = when; + newseen->spent = spent; + btree_add(&seentree, newseen); +} + +static void freeseen(void *what) +{ + seendat *s = (seendat *) what; + + Assert(s); + Assert(s->nick); + Assert(s->host); + Assert(s->chan); + Assert(s->msg); + + nfree(s->nick); + nfree(s->host); + nfree(s->chan); + nfree(s->msg); + nfree(s); +} + +static int expmemseen(void *what) +{ + int size = 0; + seendat *d = (seendat *) what; + + size += sizeof(seendat); + size += strlen(d->nick) + 1; + size += strlen(d->host) + 1; + size += strlen(d->chan) + 1; + size += strlen(d->msg) + 1; + return size; +} + +// finds a seen entry in the tree +seendat findseen_temp; +static seendat *findseen(char *nick) +{ + findseen_temp.nick = nick; + return btree_get(&seentree, &findseen_temp); +} + +// function to find all nicks that match a host +// (calls btree_getall() which calls a target function for each item) +// host: user's hostmask (used if search query doesn't contain any wildcards) +// mask: search mask +// wild: defines if we want to use the mask, or host for the search +static char *wildmatch_host, *wildmatch_mask; +int wildmatch_wild; +static void wildmatch_seens(char *host, char *mask, int wild) +{ + wildmatch_host = host; + wildmatch_mask = mask; + wildmatch_wild = wild; + btree_getall(&seentree, process_wildmatch_seens); +} + +/* process_wildmatch_seens(): + * gets called from the binary tree for each existing item. + */ +static void process_wildmatch_seens(void *data) +{ + seendat *s = (seendat *) data; + + if ((numresults > max_matches) && (max_matches > 0)) // Don't return too many + return; // matches... + if (!wildmatch_wild) { + if (wild_match(wildmatch_host, s->host)) + add_seenresult(s); + } else { + temp_wildmatch_host = my_realloc(temp_wildmatch_host, strlen(s->nick) + 1 + strlen(s->host) + 1); + strcpy(temp_wildmatch_host, s->nick); + strcat(temp_wildmatch_host, "!"); + strcat(temp_wildmatch_host, s->host); + if (wild_match(wildmatch_mask, s->nick) || wild_match(wildmatch_mask, temp_wildmatch_host)) + add_seenresult(s); + } +} + +// write seendata in the datafile +FILE *write_seen_tree_target; +static void write_seen_tree(void *data) +{ + seendat *node = (seendat *) data; + + /* format: "! nick host chan type when spent msg" */ + fprintf(write_seen_tree_target, "! %s %s %s %d %lu %d %s\n", node->nick, + node->host, node->chan, node->type, node->when, node->spent, + node->msg); +} + +// recursive function to remove old data +// QUESTION: What happens if one of the nodes get moved by killseen()? +// Possible bug/crash? +// I think it should not be a problem. When killseen() is called the +// first time, recursion already reached its end and no pointers +// are accessed anymore. But I'm not sure... maybe I'm wrong. +static void purge_seen_tree(void *data) +{ + seendat *node = (seendat *) data; + + if ((now - node->when) > (expire_seens * 86400)) { + debug1("seen data for %s has expired.", node->nick); + btree_remove(&seentree, node); + } +} + +// counts the number of nicks in the database +static int count_seens_temp; +static int count_seens() +{ + count_seens_temp = 0; + btree_getall(&seentree, _count_seens); + return count_seens_temp; +} + +static void _count_seens(void *node) +{ + count_seens_temp++; +} + +static int tcl_killseen STDVAR +{ + Context; + BADARGS(2, 2, " nick"); + findseen_temp.nick = argv[1]; + btree_remove(&seentree, &findseen_temp); + return TCL_OK; +} + +static tcl_cmds seendebugtcls[] = +{ + {"killseen", tcl_killseen}, + {0, 0} +}; diff -Nur src/mod/gseen.mod/sensors.c src/mod/gseen.mod/sensors.c --- ./src/mod/gseen.mod/sensors.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/sensors.c 2002-10-26 13:18:00.000000000 +0200 @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static int get_spent(char *nick, char *chan) +{ + struct chanset_t *ch = NULL; + memberlist *m = NULL; + + int spent; + ch = findchan_by_dname(chan); + if (ch) + m = ismember(ch, nick); + if (m && m->joined) + spent = now - m->joined; + else + spent = -1; + return spent; +} + +static int secretchan(char *chan) +{ + struct chanset_t *ch; + + ch = findchan_by_dname(chan); + if (!ch) + return 0; + if (ch->status & CHAN_SECRET) + return 1; + return 0; +} + +static int nolog(char *chan) +{ + char buf[121], *b; + + Context; + strncpy(buf, no_log, 120); + buf[120] = 0; + b = buf; + while (b[0]) + if (!strcasecmp(chan, newsplit(&b))) + return 1; +#if EGG_IS_MIN_VER(10503) + if (ngetudef("noseendata", chan)) + return 1; +#endif + return 0; +} + +static int gseen_join(char *nick, char *uhost, char *hand, char *chan) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_JOIN, nick, uhost, chan, "", now, get_spent(nick, chan)); + report_seenreq(chan, nick); + if ((hand[0] == '*') && strcasecmp(nick, hand)) + report_seenreq(chan, hand); + return 0; +} + +static int gseen_kick(char *nick, char *uhost, char *hand, char *chan, + char *victim, char *reason) +{ + struct chanset_t *ch = NULL; + memberlist *m = NULL; + char msg[1024], *s; + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + ch = findchan_by_dname(chan); + if (!ch) { + debug2("Unable to seen %s getting kicked from %s", victim, chan); + return 0; + } + if (secretchan(chan)) + chan = buf; + s = msg; + s[0] = 0; + m = ismember(ch, victim); + if (!m) { + debug2("Unable to seen %s getting kicked from %s", victim, chan); + return 0; + } + if ((strlen(nick) + strlen(reason) + 2) < 1024) + sprintf(s, "%s %s", nick, reason); + add_seen(SEEN_KICK, victim, m->userhost, chan, s, now, + get_spent(victim, chan)); + return 0; +} + +static int gseen_nick(char *nick, char *uhost, char *hand, char *chan, + char *newnick) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_NICK, nick, uhost, chan, newnick, now, get_spent(nick, chan)); + if (!(use_handles && (hand[0] != '*'))) + add_seen(SEEN_NCKF, newnick, uhost, chan, nick, now, get_spent(nick, chan)); + report_seenreq(chan, newnick); + if ((hand[0] != '*') && strcasecmp(newnick, hand)) + report_seenreq(chan, hand); + return 0; +} + +#if EGG_IS_MIN_VER(10502) +static int gseen_part(char *nick, char *uhost, char *hand, char *chan, + char *reason) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_PART, nick, uhost, chan, reason, now, get_spent(nick, chan)); + return 0; +} +#else +static int gseen_part(char *nick, char *uhost, char *hand, char *chan) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_PART, nick, uhost, chan, "", now, get_spent(nick, chan)); + return 0; +} +#endif + +static int gseen_sign(char *nick, char *uhost, char *hand, char *chan, + char *reason) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_SIGN, nick, uhost, chan, reason, now, get_spent(nick, chan)); + return 0; +} + +static int gseen_splt(char *nick, char *uhost, char *hand, char *chan) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_SPLT, nick, uhost, chan, "", now, get_spent(nick, chan)); + return 0; +} + +static int gseen_rejn(char *nick, char *uhost, char *hand, char *chan) +{ + char buf[10] = "[secret]"; + + Context; + if (nolog(chan)) + return 0; + if (use_handles && (hand[0] != '*')) + nick = hand; + if (secretchan(chan)) + chan = buf; + add_seen(SEEN_REJN, nick, uhost, chan, "", now, get_spent(nick, chan)); + return 0; +} + +static int gseen_chjn STDVAR +{ + Context; + BADARGS(7, 7, " bot hand chan flag idx host"); + add_seen(SEEN_CHJN, argv[2], argv[6], argv[3], argv[1], now, -1); + return 0; +} + +static int gseen_chpt STDVAR +{ + Context; + BADARGS(5, 5, " bot hand idx chan"); + add_seen(SEEN_CHPT, argv[2], "unknown", argv[4], argv[1], now, -1); + return 0; +} + +static cmd_t seen_kick[] = +{ + {"*", "", (Function) gseen_kick, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_nick[] = +{ + {"*", "", (Function) gseen_nick, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_join[] = +{ + {"*", "", (Function) gseen_join, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_part[] = +{ + {"*", "", (Function) gseen_part, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_sign[] = +{ + {"*", "", (Function) gseen_sign, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_splt[] = +{ + {"*", "", (Function) gseen_splt, "gseen"}, + {0, 0, 0, 0} +}; + +static cmd_t seen_rejn[] = +{ + {"*", "", (Function) gseen_rejn, "gseen"}, + {0, 0, 0, 0} +}; diff -Nur src/mod/gseen.mod/slang.c src/mod/gseen.mod/slang.c --- ./src/mod/gseen.mod/slang.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang.c 2002-10-26 13:18:03.000000000 +0200 @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static struct slang_header *slang_find(struct slang_header *, char *); + +#include "slang_text.c" +#include "slang_multitext.c" +#include "slang_ids.c" +#ifndef SLANG_NOTYPES +#include "slang_types.c" +#endif +#include "slang_duration.c" +#ifndef SLANG_NOFACTS +#include "slang_facts_places.c" +#include "slang_facts.c" +#endif +#include "slang_chanlang.c" + + +struct slang_header { + struct slang_header *next; + char *lang; + char *desc; + struct slang_id *ids; +#ifndef SLANG_NOTYPES + struct slang_type *types; +#endif + struct slang_duration *durations; +}; + +static void slang_glob_init() +{ + glob_slang_cmd_list = NULL; +} + +static int slang_glob_expmem() +{ + return slang_commands_list_expmem(glob_slang_cmd_list); +} + +static void slang_glob_free() +{ + slang_commands_list_free(glob_slang_cmd_list); + glob_slang_cmd_list = NULL; +} + +static struct slang_header *slang_create(struct slang_header *list, char *lang, char *desc) +{ + struct slang_header *nslang, *l; + + Assert(lang); + debug2("Creating language '%s' starting by %d", lang, (int) list); + for (nslang = list; nslang; nslang = nslang->next) + if (!strcasecmp(nslang->lang, lang)) + return list; + nslang = nmalloc(sizeof(struct slang_header)); + nslang->next = NULL; + nslang->desc = NULL; + nslang->lang = nmalloc(strlen(lang) + 1); + strcpy(nslang->lang, lang); + nslang->desc = nmalloc(strlen(desc) + 1); + strcpy(nslang->desc, desc); + nslang->ids = NULL; +#ifndef SLANG_NOTYPES + nslang->types = NULL; +#endif + nslang->durations = NULL; + for (l = list; l && l->next; l = l->next); + if (l) + l->next = nslang; + else { + Assert(!list); + list = nslang; + } + return list; +} + +static int slang_expmem(struct slang_header *what) +{ + int size = 0; + + while (what) { + size += sizeof(struct slang_header); + size += strlen(what->lang) + 1; + size += strlen(what->desc) + 1; + size += slang_id_expmem(what->ids); +#ifndef SLANG_NOTYPES + size += slang_type_expmem(what->types); +#endif + size += slang_duration_expmem(what->durations); + what = what->next; + } + return size; +} + +static void slang_free(struct slang_header *what) +{ + struct slang_header *next; + + while (what) { + next = what->next; + slang_id_free(what->ids); +#ifndef SLANG_NOTYPES + slang_type_free(what->types); +#endif + slang_duration_free(what->durations); + nfree(what->lang); + nfree(what->desc); + nfree(what); + what = next; + } +} + +static int slang_load(struct slang_header *slang, char *filename) +{ + FILE *f; + char *buffer, *s; + char *cmd, *sid, *strtol_ret; +#ifndef SLANG_NOTYPES + char *type; +#endif + int line, id; + + Assert(slang); + putlog(LOG_MISC, "*", "Loading language \"%s\" from %s...", slang->lang, filename); + f = fopen(filename, "r"); + if (!f) { + putlog(LOG_MISC, "*", "Couldn't open slangfile \"%s\"!", filename); + return 0; + } + buffer = nmalloc(2000); + line = 0; + while (!feof(f)) { + s = buffer; + if (fgets(s, 2000, f)) { + line++; + // at first, kill those stupid line feeds and carriage returns... + if (s[strlen(s) - 1] == '\n') + s[strlen(s) - 1] = 0; + if (s[strlen(s) - 1] == '\r') + s[strlen(s) - 1] = 0; + if (!s[0]) + continue; + cmd = newsplit(&s); + + if (!strcasecmp(cmd, "T")) { +#ifndef SLANG_NOTYPES + type = newsplit(&s); + slang->types = slang_type_add(slang->types, type, s); +#endif + } else if (!strcasecmp(cmd, "D")) { + sid = newsplit(&s); + id = strtol(sid, &strtol_ret, 10); + if (strtol_ret == sid) { + putlog(LOG_MISC, "*", "ERROR in slangfile \"%s\", line %d: %s is not a valid " + "duration index!", filename, line, sid); + continue; + } + slang->durations = slang_duration_add(slang->durations, id, s); + } else { + id = strtol(cmd, &strtol_ret, 10); + if (strtol_ret == cmd) + continue; + slang->ids = slang_id_add(slang->ids, id, s); + } + } + } + fclose(f); + nfree(buffer); + return 1; +} + +static struct slang_header *slang_find(struct slang_header *where, char *language) +{ + struct slang_header *slang = NULL; + + // at first, search for the specified language + for (slang = where; slang; slang = slang->next) + if (!strcasecmp(slang->lang, language)) + return slang; + // oops... language seems to be invalid. Let's find the default. + Assert(default_slang); + for (slang = where; slang; slang = slang->next) + if (!strcasecmp(slang->lang, default_slang)) + return slang; + // default_slang wasn't found either? *sigh* + // Let's return the first known language then. + return where; +} + +#ifndef SLANG_NOVALIDATE +/* slang_valid(): + * check if the given language is a valid one + */ +static int slang_valid(struct slang_header *where, char *language) +{ + struct slang_header *slang = NULL; + + for (slang = where; slang; slang = slang->next) + if (!strcasecmp(slang->lang, language)) + return 1; + return 0; +} +#endif + +static char getslang_error[12]; +static char *getslang(int id) +{ + char *text; + + if (!glob_slang) { + putlog(LOG_MISC, "*", "WARNING! No language selected! (getslang())"); + return "NOLANG"; + } + text = slang_id_get(glob_slang->ids, id); + if (!text) { + snprintf(getslang_error, sizeof(getslang_error), "SLANG%d", id); + return getslang_error; + } + return text; +} + +static char *getdur(int idx) +{ + char *text; + + Assert((idx >= 0) && (idx < DURATIONS)); + if (!glob_slang) { + putlog(LOG_MISC, "*", "WARNING! No language selected! (getdur())"); + return "NOLANG"; + } + text = slang_duration_get(glob_slang->durations, idx); + if (!text) { + snprintf(getslang_error, sizeof(getslang_error), "DUR%d", idx); + return getslang_error; + } + return text; +} + +#ifndef SLANG_NOTYPES +static char *getslangtype(char *type) +{ + char *stype; + + if (!glob_slang) { + putlog(LOG_MISC, "*", "WARNING! No language selected! (getslangtype())"); + return "NOLANG"; + } + stype = slang_type_get(glob_slang->types, type); + if (stype) + return stype; + else + return type; +} + +static int slangtypetoi(char *slangtype) +{ + char *type; + + if (!glob_slang) { + putlog(LOG_MISC, "*", "WARNING! No language selected! (slangtypetoi())"); + return T_ERROR; + } + type = slang_type_slang2type(glob_slang->types, slangtype); + if (type) { + debug1("type: %s", type); + return typetoi(type); + } else + return typetoi(slangtype); +} +#endif + +#ifndef SLANG_NOGETALL +static char *getslang_first(int id) +{ + char *text; + + if (!glob_slang) { + putlog(LOG_MISC, "*", "WARNING! No language selected! (getslang())"); + return "NOLANG"; + } + text = slang_id_get_first(glob_slang->ids, id); + if (!text) { + snprintf(getslang_error, sizeof(getslang_error), "SLANG%d", id); + return getslang_error; + } + return text; +} + +static char *getslang_next() +{ + return slang_id_get_next(); +} +#endif diff -Nur src/mod/gseen.mod/slang_chanlang.c src/mod/gseen.mod/slang_chanlang.c --- ./src/mod/gseen.mod/slang_chanlang.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_chanlang.c 2002-10-26 13:18:02.000000000 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +struct slang_chanlang { + struct slang_chanlang *next; + char *chan; + char *lang; +}; + +static struct slang_chanlang *chanlangs = NULL; + +static struct slang_chanlang *slang_chanlang_add(struct slang_chanlang *, char *, char *); +static int slang_chanlang_expmem(struct slang_chanlang *); +static void slang_chanlang_free(struct slang_chanlang *); +static char *slang_chanlang_get(struct slang_chanlang *, char *); + +static struct slang_chanlang *slang_chanlang_add(struct slang_chanlang *where, char *chan, char *lang) +{ + struct slang_chanlang *item; + + for (item = where; item; item = item->next) + if (!rfc_casecmp(item->chan, chan)) + break; + if (!item) { + item = nmalloc(sizeof(struct slang_chanlang)); + item->chan = nmalloc(strlen(chan) + 1); + strcpy(item->chan, chan); + item->lang = nmalloc(strlen(lang) + 1); + strcpy(item->lang, lang); + item->next = where; + where = item; + } else { + Assert(item->lang); + item->lang = nrealloc(item->lang, strlen(lang) + 1); + strcpy(item->lang, lang); + } + return where; +} + +static int slang_chanlang_expmem(struct slang_chanlang *what) +{ + int size = 0; + + while (what) { + Assert(what); + Assert(what->chan); + Assert(what->lang); + size += sizeof(struct slang_chanlang); + size += strlen(what->chan) + 1; + size += strlen(what->lang) + 1; + what = what->next; + } + return size; +} + +static void slang_chanlang_free(struct slang_chanlang *what) +{ + struct slang_chanlang *next; + + while (what) { + Assert(what); + Assert(what->chan); + Assert(what->lang); + next = what->next; + nfree(what->chan); + nfree(what->lang); + nfree(what); + what = next; + } +} + +static char *slang_chanlang_get(struct slang_chanlang *where, char *chan) +{ + while (where) { + if (!rfc_casecmp(where->chan, chan)) + return where->lang; + where = where->next; + } + return default_slang; +} + +/* slang_getbynick(): + * tries to find an appropriate language for nick by searching + * him on a channel and using the language of this channel. + */ +static struct slang_header *slang_getbynick(struct slang_header *where, char *nick) +{ + struct chanset_t *chan; + + for (chan = chanset; chan; chan = chan->next) + if (ismember(chan, nick)) +#if EGG_IS_MIN_VER(10500) + return slang_find(where, slang_chanlang_get(chanlangs, chan->dname)); +#else + return slang_find(where, slang_chanlang_get(chanlangs, chan->name)); +#endif + return slang_find(where, default_slang); +} diff -Nur src/mod/gseen.mod/slang_duration.c src/mod/gseen.mod/slang_duration.c --- ./src/mod/gseen.mod/slang_duration.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_duration.c 2002-10-26 13:18:01.000000000 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define DURATIONS 13 + +struct slang_duration { + char *durs[DURATIONS]; +}; + +static struct slang_duration *slang_duration_add(struct slang_duration *where, int idx, char *text) +{ + int i; + + if ((idx < 0) || (idx >= DURATIONS)) { + putlog(LOG_MISC, "*", "Warning: Invalid duration index \"%d\".", idx); + return where; + } + debug2("Adding duration[%d]: %s", idx, text); + if (!where) { + where = nmalloc(sizeof(struct slang_duration)); + for (i = 0; i < DURATIONS; i++) + where->durs[i] = NULL; + } + if (where->durs[idx]) + nfree(where->durs[idx]); + where->durs[idx] = nmalloc(strlen(text) + 1); + strcpy(where->durs[idx], text); + return where; +} + +static int slang_duration_expmem(struct slang_duration *what) +{ + int i, size = 0; + + if (!what) + return 0; + size += sizeof(struct slang_duration); + for (i = 0; i < DURATIONS; i++) + if (what->durs[i]) + size += strlen(what->durs[i]) + 1; + return size; +} + +static void slang_duration_free(struct slang_duration *what) +{ + int i; + + if (what) { + for (i = 0; i < DURATIONS; i++) + if (what->durs[i]) + nfree(what->durs[i]); + nfree(what); + } +} + +static char *slang_duration_get(struct slang_duration *where, int idx) +{ + if (!where) { + debug0("no where"); + return NULL; + } + if ((idx < 0) || (idx >= DURATIONS)) { + debug1("invalid duration index: %d", idx); + return NULL; + } + return where->durs[idx]; +} diff -Nur src/mod/gseen.mod/slang_gseen_commands.c src/mod/gseen.mod/slang_gseen_commands.c --- ./src/mod/gseen.mod/slang_gseen_commands.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_gseen_commands.c 2002-10-26 13:18:06.000000000 +0200 @@ -0,0 +1,235 @@ +static void slang_send_botnick() +{ + strncat(slang_text_buf, botname, sizeof(slang_text_buf)); +} + +static void slang_send_query() +{ + if (glob_query) + strncat(slang_text_buf, glob_query, sizeof(slang_text_buf)); +} + +static void slang_send_laston() +{ + if (glob_laston) + strncat(slang_text_buf, glob_laston, sizeof(slang_text_buf)); +} + +static void slang_send_otherchan() +{ + if (glob_otherchan) + strncat(slang_text_buf, glob_otherchan, sizeof(slang_text_buf)); +} + +static void slang_send_othernick() +{ + if (glob_othernick) + strncat(slang_text_buf, glob_othernick, sizeof(slang_text_buf)); +} + +static void slang_send_remotebot() +{ + if (glob_remotebot) + strncat(slang_text_buf, glob_remotebot, sizeof(slang_text_buf)); +} + +static void slang_send_snick() +{ + if (glob_seendat) + strncat(slang_text_buf, glob_seendat->nick, sizeof(slang_text_buf)); +} + +static void slang_send_shost() +{ + if (glob_seendat) + strncat(slang_text_buf, glob_seendat->host, sizeof(slang_text_buf)); +} + +static void slang_send_schan() +{ + if (glob_seendat) + strncat(slang_text_buf, glob_seendat->chan, sizeof(slang_text_buf)); +} + +static void slang_send_swhen() +{ + char *dur; + + if (glob_seendat) { + dur = gseen_duration(now - glob_seendat->when); + strncat(slang_text_buf, dur, sizeof(slang_text_buf)); + } +} + +static void slang_send_stime() +{ + time_t tt; + char t[20]; + + if (glob_seendat) { + tt = glob_seendat->when; + strftime(t, 19, "%d.%m. %H:%M", localtime(&tt)); + strncat(slang_text_buf, t, sizeof(slang_text_buf)); + } +} + +static void slang_send_spent() +{ + char *dur; + + if (glob_seendat) { + dur = gseen_duration(glob_seendat->spent); + strncat(slang_text_buf, dur, sizeof(slang_text_buf)); + } +} + +static void slang_send_smsg() +{ + if (glob_seendat) + strncat(slang_text_buf, glob_seendat->msg, sizeof(slang_text_buf)); +} + +static void slang_send_numresults() +{ + char buf[7]; + + snprintf(buf, sizeof(buf), "%d", numresults); + strncat(slang_text_buf, buf, sizeof(slang_text_buf)); +} + +static void slang_send_punisher() +{ + char *reason; + int len; + + if (glob_seendat) { + reason = strchr(glob_seendat->msg, ' '); + if (!reason) + strncat(slang_text_buf, glob_seendat->msg, sizeof(slang_text_buf)); + else { + len = (int) reason - (int) glob_seendat->msg; + strncat(slang_text_buf, glob_seendat->msg, (sizeof(slang_text_buf) < len) ? sizeof(slang_text_buf) : len); + } + } +} + +static void slang_send_kickreason() +{ + char *reason; + + if (glob_seendat) { + reason = strchr(glob_seendat->msg, ' '); + if (reason) + strncat(slang_text_buf, reason, sizeof(slang_text_buf)); + } +} + +static void slang_send_rnick() +{ + if (glob_seenrequest) { + Assert(glob_seenrequest->by); + Assert(glob_seenrequest->by->who); + strncat(slang_text_buf, glob_seenrequest->by->who, sizeof(slang_text_buf)); + } +} + +static void slang_send_rchan() +{ + if (glob_seenrequest) { + Assert(glob_seenrequest->by); + Assert(glob_seenrequest->by->chan); + strncat(slang_text_buf, glob_seenrequest->by->chan, sizeof(slang_text_buf)); + } +} + +static void slang_send_rhost() +{ + if (glob_seenrequest) { + Assert(glob_seenrequest->by); + Assert(glob_seenrequest->by->host); + strncat(slang_text_buf, glob_seenrequest->by->host, sizeof(slang_text_buf)); + } +} + +static void slang_send_rtime() +{ + time_t tt; + char t[20]; + + if (glob_seenrequest) { + Assert(glob_seenrequest->by); + tt = glob_seenrequest->by->when; + strftime(t, sizeof(t), "%d.%m. %H:%M", localtime(&tt)); + strncat(slang_text_buf, t, sizeof(slang_text_buf)); + } +} + +static void slang_send_rwhen() +{ + if (glob_seenrequest) { + Assert(glob_seenrequest->by); + strncat(slang_text_buf, gseen_duration(now - glob_seenrequest->by->when), sizeof(slang_text_buf)); + } +} + +static void slang_send_requests() +{ + char buf[7]; + + snprintf(buf, sizeof(buf), "%d", glob_seenrequests); + strncat(slang_text_buf, buf, sizeof(slang_text_buf)); +} + +static void slang_send_totalnicks() +{ + char buf[7]; + + snprintf(buf, sizeof(buf), "%d", glob_totalnicks); + strncat(slang_text_buf, buf, sizeof(slang_text_buf)); +} + +static void slang_send_totalbytes() +{ + char buf[20]; + + snprintf(buf, sizeof(buf), "%d", glob_totalbytes); + strncat(slang_text_buf, buf, sizeof(slang_text_buf)); +} + +static void slang_send_nick() +{ + if (glob_nick) + strncat(slang_text_buf, glob_nick, sizeof(slang_text_buf)); +} + +struct slang_text_commands slang_text_gseen_command_table[] = +{ + {"botnick", slang_send_botnick}, + {"query", slang_send_query}, + {"laston", slang_send_laston}, + {"otherchan", slang_send_otherchan}, + {"othernick", slang_send_othernick}, + {"remotebot", slang_send_remotebot}, + {"snick", slang_send_snick}, + {"swhen", slang_send_swhen}, + {"stime", slang_send_stime}, + {"shost", slang_send_shost}, + {"schan", slang_send_schan}, + {"spent", slang_send_spent}, + {"smsg", slang_send_smsg}, + {"numresults", slang_send_numresults}, + {"snick2", slang_send_smsg}, + {"bnbot", slang_send_smsg}, + {"punisher", slang_send_punisher}, + {"kickreason", slang_send_kickreason}, + {"rnick", slang_send_rnick}, + {"rchan", slang_send_rchan}, + {"rhost", slang_send_rhost}, + {"rtime", slang_send_rtime}, + {"rwhen", slang_send_rwhen}, + {"requests", slang_send_requests}, + {"totalnicks", slang_send_totalnicks}, + {"totalbytes", slang_send_totalbytes}, + {"nick", slang_send_nick}, + {0, 0} +}; diff -Nur src/mod/gseen.mod/slang_ids.c src/mod/gseen.mod/slang_ids.c --- ./src/mod/gseen.mod/slang_ids.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_ids.c 2002-10-26 13:18:04.000000000 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +struct slang_id { + struct slang_id *next; + int id; + struct slang_multitext *mtext; +}; + +static struct slang_id* slang_id_add(struct slang_id *, int, char *); +static int slang_id_expmem(struct slang_id *); +static void slang_id_free(struct slang_id *); +static char *slang_id_get(struct slang_id *, int); + +static struct slang_id* slang_id_add(struct slang_id *where, int id, char *text) +{ + struct slang_id *newitem; + + newitem = NULL; + if (where) { + for (newitem = where; newitem; newitem = newitem->next) + if (newitem->id == id) + break; + } + if (!newitem) { + newitem = nmalloc(sizeof(struct slang_id)); + newitem->next = NULL; + newitem->id = id; + newitem->mtext = NULL; + if (where) + newitem->next = where; + else + newitem->next = NULL; + where = newitem; + } + newitem->mtext = slang_mtext_add(newitem->mtext, text); + return where; +} + +static int slang_id_expmem(struct slang_id *what) +{ + int size = 0; + + for (; what; what = what->next) { + size += sizeof(struct slang_id); + size += slang_multitext_expmem(what->mtext); + } + return size; +} + +static void slang_id_free(struct slang_id *what) +{ + struct slang_id *next; + + while (what) { + next = what->next; + slang_multitext_free(what->mtext); + nfree(what); + what = next; + } +} + +static char *slang_id_get(struct slang_id *where, int i) +{ + while (where) { + if (where->id == i) + return slang_multitext_getrandomtext(where->mtext); + where = where->next; + } + return NULL; +} + +#ifndef SLANG_NOGETALL +static char *slang_id_get_first(struct slang_id *where, int id) +{ + while (where) { + if (where->id == id) { + return slang_multitext_get_first(where->mtext); + } + where = where->next; + } + return NULL; +} + +static char *slang_id_get_next() +{ + return slang_multitext_get_next(); +} +#endif diff -Nur src/mod/gseen.mod/slang_multitext.c src/mod/gseen.mod/slang_multitext.c --- ./src/mod/gseen.mod/slang_multitext.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_multitext.c 2002-10-26 13:18:05.000000000 +0200 @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +struct slang_mt_content { + struct slang_mt_content *next; + struct slang_text *text; +}; + +struct slang_multitext { + int nr; + struct slang_mt_content *contents; +}; + +static struct slang_multitext *slang_mtext_add(struct slang_multitext *, char *); +static int slang_multitext_expmem(struct slang_multitext *); +static void slang_multitext_free(struct slang_multitext *); +static char *slang_multitext_getrandomtext(struct slang_multitext *); +#ifndef SLANG_NOTYPES +static struct slang_text *slang_multitext_find(struct slang_multitext *, char *); +#endif +#ifndef SLANG_NOGETALL +static char *slang_multitext_get_first(struct slang_multitext *); +static char *slang_multitext_get_next(); +#endif + +static struct slang_multitext *slang_mtext_add(struct slang_multitext *where, char *text) +{ + struct slang_mt_content *oc, *nc; + + if (!where) { + where = nmalloc(sizeof(struct slang_multitext)); + where->nr = 0; + where->contents = NULL; + } + nc = nmalloc(sizeof(struct slang_mt_content)); + nc->next = NULL; + nc->text = slang_text_parse(text); + for (oc = where->contents; oc && oc->next; oc = oc->next); + if (oc) { + Assert(!oc->next); + oc->next = nc; + } else + where->contents = nc; + where->nr++; + return where; +} + +static int slang_multitext_expmem(struct slang_multitext *what) +{ + struct slang_mt_content *content; + int size = 0; + + if (!what) { + debug0("WARNING! slang_multitext_expmem() called with NULL pointer!"); + return 0; + } + size += sizeof(struct slang_multitext); + for (content = what->contents; content; content = content->next) { + size += sizeof(struct slang_mt_content); + size += slang_text_expmem(content->text); + } + return size; +} + +static void slang_multitext_free(struct slang_multitext *what) +{ + struct slang_mt_content *content, *next; + + if (!what) { + debug0("WARNING! slang_multitext_free() called with NULL pointer!"); + return; + } + content = what->contents; + while (content) { + next = content->next; + slang_text_free(content->text); + nfree(content); + content = next; + } + nfree(what); +} + +static char *slang_multitext_getrandomtext(struct slang_multitext *where) +{ + struct slang_mt_content *content; + unsigned long x; + + if (!where) + return NULL; + x = random() % where->nr; + for (content = where->contents; content; content = content->next) + if (!x) + return slang_text_get(content->text); + else + x--; + // we should never reach this part + debug0("warning: getrandomtext didn't find anything!"); + return NULL; +} + +#ifndef SLANG_NOTYPES +static struct slang_text *slang_multitext_find(struct slang_multitext *where, char *what) +{ + struct slang_mt_content *content; + + Assert(where); + for (content = where->contents; content; content = content->next) { + Assert(content->text); + if (!slang_text_strcasecmp(content->text, what)) + return content->text; + } + return NULL; +} +#endif + +#ifndef SLANG_NOGETALL +static struct slang_mt_content *glob_mtext_content; +static char *slang_multitext_get_first(struct slang_multitext *where) +{ + Assert(where); + glob_mtext_content = where->contents; + if (glob_mtext_content) + return slang_text_get(glob_mtext_content->text); + else + return NULL; +} + +static char *slang_multitext_get_next() +{ + glob_mtext_content = glob_mtext_content->next; + if (glob_mtext_content) + return slang_text_get(glob_mtext_content->text); + else + return NULL; +} +#endif diff -Nur src/mod/gseen.mod/slang_text.c src/mod/gseen.mod/slang_text.c --- ./src/mod/gseen.mod/slang_text.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/slang_text.c 2002-10-26 13:18:07.000000000 +0200 @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +struct slang_text { + struct slang_text *next; + char *string; + void (*command) (); +}; + +struct slang_text_commands { + char *command; + void (*targetfunc) (); +}; + +struct slang_command_list { + struct slang_command_list *next; + struct slang_text_commands *commands; +}; + +static struct slang_text *slang_text_parse(char *); +static struct slang_text *slang_text_create(struct slang_text *); +static void slang_text_add_string(struct slang_text *, char *); +static void slang_text_add_command(struct slang_text *, char *); +static void slang_text_free(struct slang_text *); +static int slang_text_expmem(struct slang_text *); +static char *slang_text_get(struct slang_text *); +#ifndef SLANG_NOTYPES +static int slang_text_strcasecmp(struct slang_text *, char *); +#endif + +static struct slang_text *slang_text_parse(char *text) +{ + char *cmdstart, *cmdend; + struct slang_text *firstitem, *item; + + firstitem = slang_text_create(NULL); + item = firstitem; + while ((cmdstart = strstr(text, ""); + if (!cmdend) { + putlog(LOG_MISC, "*", "ERROR parsing slang text: unterminated command \"%s\"!", cmdstart); + break; + } + cmdend[0] = 0; + slang_text_add_command(item, cmdstart); + item = slang_text_create(item); + text = cmdend + 3; + } + slang_text_add_string(item, text); + return firstitem; +} + +static struct slang_text *slang_text_create(struct slang_text *where) +{ + struct slang_text *newpart; + + newpart = nmalloc(sizeof(struct slang_text)); + newpart->next = NULL; + newpart->string = NULL; + newpart->command = NULL; + while (where && where->next) + where = where->next; + if (where) + where->next = newpart; + return newpart; +} + +static void slang_text_add_string(struct slang_text *item, char *s) +{ + Assert(item); + Assert(!item->string); + item->string = nmalloc(strlen(s) + 1); + strcpy(item->string, s); +} + +static void slang_text_free(struct slang_text *item) +{ + if (!item) + return; + slang_text_free(item->next); + if (item->string) + nfree(item->string); + nfree(item); +} + +static int slang_text_expmem(struct slang_text *item) +{ + int size = 0; + + while (item) { + size += sizeof(struct slang_text); + if (item->string) + size += strlen(item->string) + 1; + item = item->next; + } + return size; +} + +#ifndef SLANG_NOTYPES +static int slang_text_strcasecmp(struct slang_text *item, char *text) +{ + Assert(item); + debug2("s_t_sc: '%s', '%s'", text, item->string); + if (item->command || item->next) + return 1; + return strcasecmp(item->string, text); +} +#endif + +static char slang_text_buf[500]; +static char *slang_text_get(struct slang_text *item) +{ + slang_text_buf[0] = 0; + while (item) { + if (item->string) + strncat(slang_text_buf, item->string, sizeof(slang_text_buf)); + else if (item->command) + item->command(); + item = item->next; + } + return slang_text_buf; +} + +/*****************************************************/ + + +static struct slang_command_list *glob_slang_cmd_list; + +static struct slang_command_list *slang_commands_list_add(struct slang_command_list *where, struct slang_text_commands *what) +{ + struct slang_command_list *newcommandlist; + + newcommandlist = nmalloc(sizeof(struct slang_command_list)); + newcommandlist->commands = what; + newcommandlist->next = where; + return newcommandlist; +} + +static int slang_commands_list_expmem(struct slang_command_list *what) +{ + int size = 0; + + while (what) { + size += sizeof(struct slang_command_list); + what = what->next; + } + return size; +} + +static void slang_commands_list_free(struct slang_command_list *what) +{ + struct slang_command_list *next; + + while (what) { + next = what->next; + nfree(what); + what = next; + } +} + +static void slang_text_add_command(struct slang_text *item, char *s) +{ + struct slang_command_list *cmdlist; + char *cmd; + int i; + + cmd = newsplit(&s); + i = 0; + for (cmdlist = glob_slang_cmd_list; cmdlist; cmdlist = cmdlist->next) { + for (i = 0; 1; i++) { + if (!cmdlist->commands[i].command) + break; + if (!strcasecmp(cmdlist->commands[i].command, cmd)) { + item->command = cmdlist->commands[i].targetfunc; + return; + } + } + } + putlog(LOG_MISC, "*", "ERROR! Unknown slang-command: '%s'", cmd); +} diff -Nur src/mod/gseen.mod/tclcmds.c src/mod/gseen.mod/tclcmds.c --- ./src/mod/gseen.mod/tclcmds.c 1970-01-01 01:00:00.000000000 +0100 +++ ./src/mod/gseen.mod/tclcmds.c 2002-10-26 13:18:08.000000000 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2000,2001 Florian Sander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +static int tcl_setchanseenlang STDVAR +{ + Context; + BADARGS(3, 3, " channel language"); + chanlangs = slang_chanlang_add(chanlangs, argv[1], argv[2]); + return TCL_OK; +} + +static int tcl_loadseenslang STDVAR +{ +// int ret = 0; + char *shortname, *longname, *filename; + struct slang_header *slang; + + Context; + BADARGS(4, 4, " language description langfile"); + shortname = argv[1]; + longname = argv[2]; + filename = argv[3]; + coreslangs = slang_create(coreslangs, shortname, longname); + slang = slang_find(coreslangs, shortname); + Assert(slang); + if (!slang_load(slang, filename)) { + Tcl_AppendResult(irp, "Couldn't open seenslang file!!!", NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +static tcl_cmds gseentcls[] = +{ + {"loadseenslang", tcl_loadseenslang}, + {"setchanseenlang", tcl_setchanseenlang}, + {0, 0} +};