diff options
Diffstat (limited to 'main/eggdrop/gseen.mod.patch')
-rw-r--r-- | main/eggdrop/gseen.mod.patch | 5123 |
1 files changed, 5123 insertions, 0 deletions
diff --git a/main/eggdrop/gseen.mod.patch b/main/eggdrop/gseen.mod.patch new file mode 100644 index 000000000..e70aef887 --- /dev/null +++ b/main/eggdrop/gseen.mod.patch @@ -0,0 +1,5123 @@ +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 <nick> ++ I think this command doesn't need an explanation. ^_^ ++!seen <mask> ++ Searches the database for entries that match <mask> ++ for example "!seen *!user@dialin-*.isp.com" ++!seennick <nick> ++ !seen also checks if a user was online later with a ++ different nick. !seennick only seens for <nick> ++!seenstats ++ just a little report on how many nicks are tracked ++ ++All commands are also accessible via /msg. ++("/msg <bot> seen <nick>", for example) ++ ++ ++AI seen: ++-------- ++ ++This module has a simple built in AI routine. ++A short example: ++ ++<G`Quann> 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 <nick> <uhost> <hand> <chan> <text> ++ 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<g>) 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 <stdlib.h> ++#include <sys/stat.h> ++#include <time.h> /* 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 "<bot> 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 <?nick/?>, ++# für Antworten, die per NOTICE an den User geschickt werden: ++11 ++# für Antworten auf Anfragen, die per "/msg <bot> 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 <?query/?> jetzt hier nicht sehen kannst, dann brauchst Du sicherlich eine neue Brille ^_^ ++56 ich muss mir unbedingt mal die Tarnkappe von <?query/?> 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, <?query/?> gesehen zu haben... ++65 <?query/?>? 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' <?query/?> seit <?laston/?> 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 <?query/?> ist grade unter dem Nick "<?othernick/?>" in diesem Channel zu finden. ++74 <?query/?> ist gerade in <?otherchan/?>. ++75 Deine Anfrage führte zu genau einem Ergebnis: ++76 Immerhin <?numresults/?> Treffer ergab deine Anfrage: ++77 Wow, auf deine Anfrage passen sogar <?numresults/?> Einträge in meiner Datenbank! Dies sind die 5 aktuellsten: ++ ++# ++## falls ein anderer Bot etwas gefunden hat: ++# ++85 <?remotebot/?> sagt: ++ ++# ++## die eigentliche Information ++# ++101 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> betreten sehen (<?stime/?>). <?snick/?> ist noch immer da. ++121 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> betreten sehen (<?stime/?>), aber <?snick/?> verschwand mysteriöserweise. ++102 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> nach <?spent/?> verchatteter Zeit verlassen sehen (<?stime/?>) ++103 Ich habe <?snick/?> (<?shost/?>) zuletzt in <?schan/?> gesehen, als er/sie vor <?swhen/?> (<?stime/?>) nach <?spent/?> das IRC verließ ("<?smsg/?>"). ++104 Zuletzt habe ich <?snick/?> (<?shost/?>) vor <?swhen/?> in <?schan/?> gesehen, den Nick zu <?snick2/?> wechselnd. <?snick2/?> ist noch immer dort. ++124 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick2/?> mysteriously dematerialized. ++105 Zuletzt habe ich <?snick/?> (<?shost/?>) vor <?swhen/?> in <?schan/?> gesehen, den Nick von <?snick2/?> wechselnd. <?snick/?> ist noch immer dort. ++125 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized. ++106 Zuletzt habe ich <?snick/?> (<?shost/?>) gesehen, als er vor <?swhen/?> (<?stime/?>) von <?punisher/?> aus <?schan/?> gejagt wurde. (<?kickreason/?>) ++107 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie von <?schan/?> aus in einem Netsplit verschwand. ++108 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie nach einem Netsplit in <?schan/?> zurück kam. <?snick/?> ist noch immer dort. ++128 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie nach einem Netsplit in <?schan/?> zurück kam. Allerdings konnte <?snick/?> dem Gott der Netsplits nicht endgültig entkommen und ist wieder verschollen... ++109 <?snick/?> was last seen joining the botnet channel <?schan/?> on <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++129 <?snick/?> was last seen joining the partyline on <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++110 <?snick/?> was last seen leaving the botnet channel <?schan/?> from <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++130 <?snick/?> was last seen leaving the partyline from <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++140 <?snick/?> (<?shost/?>) was last seen on <?schan/?> <?swhen/?> ago (<?stime/?>). ++ ++# ++## Seen-Mitteilungen ++# ++170 <?rnick/?> (<?rhost/?>) scheint vor <?rwhen/?> (<?rtime/?>) in <?rchan/?> auf der Suche nach Dir gewesen zu sein. ++171 <?requests/?> Leute haben sich nach Dir erkundigt: ++172 Der/die letzte war <?rnick/?> (<?rhost/?>) in <?rchan/?> vor <?rwhen/?> (<?rtime/?>). ++ ++# ++## Statistiken ++# ++180 Momentan sind <?totalnicks/?> Nicks in meiner Datenbank. Gesamter Speicherverbrauch: <?totalbytes/?> Bytes ++180 In meiner Datenbank befinden sich <?totalnicks/?> Nicks und verbrauchen <?totalbytes/?> 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 <?nick/?>, ++# 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 <?query/?> 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 <?query/?>. ++65 <?query/?>? hmm... I'm trying to remember... maybe... I'm not sure... no. I don't remember <?query/?>. ++66 I haven't seen <?query/?> for <?laston/?>. ++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 <?query/?> is <?othernick/?>, who is on this channel right now. ++74 <?query/?> is on <?otherchan/?> right now. ++ ++# ++## results found ++# ++75 I found one match to your query: ++76 I found <?numresults/?> matches to your query: ++77 I found <?numresults/?> matches to your query. These are the 5 most recent ones: ++ ++# ++## results found by another bot in the botnet ++# ++85 <?remotebot/?> says: ++ ++# ++## the core info ++# ++101 <?snick/?> (<?shost/?>) was last seen joining <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick/?> is still there. ++121 <?snick/?> (<?shost/?>) was last seen joining <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized. ++102 <?snick/?> (<?shost/?>) was last seen parting <?schan/?> <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there. ++103 <?snick/?> (<?shost/?>) was last seen quitting <?schan/?> <?swhen/?> ago (<?stime/?>) stating "<?smsg/?>" after spending <?spent/?> there. ++104 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick2/?> is still there. ++124 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick2/?> mysteriously dematerialized. ++105 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick/?> is still there. ++125 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized. ++106 <?snick/?> (<?shost/?>) was last seen being kicked from <?schan/?> by <?punisher/?> (<?kickreason/?>) <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there. ++107 <?snick/?> (<?shost/?>) was last seen splitting from <?schan/?> <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there. ++108 <?snick/?> (<?shost/?>) was last seen rejoining <?schan/?> from a netsplit <?swhen/?> ago (<?stime/?>) <?snick/?> is still there. ++128 <?snick/?> (<?shost/?>) was last seen rejoining <?schan/?> from a netsplit <?swhen/?> ago (<?stime/?>), but the god of netsplits didn't let him escape, so he's not here now. ++109 <?snick/?> was last seen joining the botnet channel <?schan/?> on <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++129 <?snick/?> was last seen joining the partyline on <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++110 <?snick/?> was last seen leaving the botnet channel <?schan/?> from <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++130 <?snick/?> was last seen leaving the partyline from <?bnbot/?> <?swhen/?> ago (<?stime/?>). ++140 <?snick/?> (<?shost/?>) was last seen on <?schan/?> <?swhen/?> ago (<?stime/?>). ++ ++# ++## seen notification ++# ++170 <?rnick/?> (<?rhost/?>) was looking for you on <?rchan/?> <?rwhen/?> ago (<?rtime/?>). ++171 There have been <?requests/?> users looking for you: ++172 The last one was <?rnick/?> (<?rhost/?>) on <?rchan/?> <?rwhen/?> ago (<?rtime/?>). ++ ++# ++## seen stats ++# ++180 I'm currently tracking <?totalnicks/?> nicks using <?totalbytes/?> 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, "<?"))) { ++ cmdstart[0] = 0; ++ slang_text_add_string(item, text); ++ item = slang_text_create(item); ++ text += 2; ++ cmdstart += 2; ++ cmdend = strstr(cmdstart, "/?>"); ++ 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} ++}; |