[project @ 2002-07-15 21:01:51 by unknown_lamer]
authorunknown_lamer <unknown>
Mon, 15 Jul 2002 21:01:51 +0000 (21:01 +0000)
committerunknown_lamer <unknown>
Mon, 15 Jul 2002 21:01:51 +0000 (21:01 +0000)
User functions are now all stored in a map and the userFunctionStruct
data type is gone as part of the new unification. userFunction now
protects it's scmFunc if it was passed so you can use lambda's in a
bot:addcommand now. The docs are slightly (not really) better and I've
started to add lower level message sending commands to the scripting
interface. Looks like it's almost time for 2.1.0 to be released.

15 files changed:
ChangeLog
NEWS
TODO
bobot++.info
bobot++.texinfo
scripts/bobot-utils.scm
scripts/hello
source/Bot.C
source/Bot.H
source/Interp.C
source/Parser.C
source/Parser.H
source/ScriptCommands.C
source/ScriptCommands.H
source/UserCommands.C

index fb38f4c..bac2201 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2002-07-13  Clinton Ebadi  <clinton@unknownlamer.org>
+
+       * source/Bot.C (Bot): userFunctions is now a map for efficiency
+       (worst case search is now NlogN instead of N). This improvement is
+       not as drastic as the one gained by making the Parser functions
+       list a map (because we don't scan this map for every message), but
+       it helps to clean up the code
+
+2002-07-12  Clinton Ebadi  <clinton@unknownlamer.org>
+
+       * source/Parser.H: Removed userFunctionsStruct
+
+       * source/Interp.C (Startup): ScriptCommands::sendCTCP registered
+       as bot:send-CTCP for Scheme
+
+       * source/ScriptCommands.C (sendCTCP): Wrote sendCTCP
+
 2002-07-11  Clinton Ebadi  <clinton@unknownlamer.org>
 
        * source/ServerQueue.C (sendUser): Changed . . in USER command to
diff --git a/NEWS b/NEWS
index 6cd8fec..0aca854 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,7 @@ As of version 1.98 unknown_lamer is the new maintainer.
 Also as of version 1.98, you must have Guile 1.5.6+ or 1.6.x in order
 to compile scripting support.
 
-Version 2.1.0: Hook!
+Version 2.1.0: Zug Zug
 - Hooks can now be fallthrough or non fallthrough. You can set a hooks
   priority and whether or not it falls through (i.e. continues hook
   matching after it has been executed) using two optional args to
@@ -28,6 +28,8 @@ Version 2.1.0: Hook!
 - Logs are now in ~/.bobotpp/logs/
 - bot-* procedures in Scheme are now bot:*. The onl bot-* functions
   are defined as aliases in bobot-utils.scm
+- You can now use lambda's for new commands (oops, I wasn't protecting
+  the objects from garbage collection)
 
 
 Version 2.0: Stable release / CVS merges
diff --git a/TODO b/TODO
index 382d3df..eb358dc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,49 +1,38 @@
 2.2 will be released when most of these are done
 
 General:
-* Compile with -Wall and kill all warnings
-* Make String more compatible with std::string
-   - Make operator >> act like all other >>'s, and add getline
-   + Add compatibility operators to String
-* Remove gh_* and other deprecated guile stuff
+* Make String operator >> act like all other >>'s, and add getline?
 * finish ISO C++-ification (remove deprecated uses of static, casts)
-* Add Scheme utils function to convert "normal" dates into time since
-   1970.
-* Make bot-random use the Guile RNG instead of C RNG (better?)
-* Add commands to delete a hook
-   - return an iterator to the hook and let the user kill it?
-   - OR give the hook a new field ("name") and use that
-   - all part of the new hooks system I guess
-* Convert UserCommands to use Commands
-* Expand Commands to do everything the bot can do
-* Add new commands to Scheme afterwards
-* It would be interesting to see what SWIG could do here...
-   (eliminate ScriptCommands in favor a SWIG wrapper maybe?)  Why
-   though? Well, maybe not kill ScriptCommands completely.  Just make
-   it's interface a simple wrapper around Commands that automatically
-   adds the Bot* first arg as Interp::bot. This would eliminate a lot
-   of the type checking. OTOH, I'll have to look in to this more when
-   I get back.
+* Finish converting UserCommands to use Commands
 * Audit code and see what data in classes should be made private and
    have getters/setters added (e.g. logFileName in Bot--if this is
    changed the log file doesn't change after the Bot is started).
-* Fix logging stuff
+
+Scripting:
+* Make bot:random use the Guile RNG instead of C RNG (better?)
+   - definition would be (define (bot:random) (random BIG_NUMBER))
+   - Just use max-fixnum from the test scripts for BIG_NUMBER
+* Add commands to delete a hook
+   - return an iterator to the hook and let the user kill it?
+   - this would require a new SMOB to be created
+* Add Scheme utils function to convert "normal" dates into time since
+   1970.
+* Finish adding commands to Scheme for sending messages
+   (e.g. bot:send-CTCP to send a CTCP message)
+* Add util functions for doing stuff like quoting CTCP messages
 
 Networking:
 * Add a networked interface to guile repl
    - Admins only
    - SSH? Telnet? DCC-Chat?
-   - Access to repl will require use to authenticate
-   - Allow the load & reloading of scripts w/o restarting bot
+   - Access to repl will require user to authenticate
    - Allow server to be disabled at run because of security...
-* Enable DCC support (maybe not, you can always intercept CTCP from
-   Scheme)
+* Fix DCC support
 * Add Channel logging (log full text of channel if enabled)
-   - maybe not, you can do this from Scheme (it would probably be
-     really slow though)
+   - maybe do this as a script (log.scm)
 * Make connecting to irc.oftc.net work...I wonder if their ircd is b0rked
 
-Config:
+Config: (maybe)
 * Change bot.conf syntax (just load it with guile)
 (set-cmdchar #\!)
 (set-name "DumbBot")
@@ -67,4 +56,7 @@ Config:
   - Support per-server channels, logfiles, cmdchars, everything...
 
 Documentation:
-- Texinfo manual (including scripting section)
\ No newline at end of file
+- Work on Texinfo manual (including scripting section)
+
+Other (post 2.2 release):
+* Remove gh_* when Guile 1.8 is released
\ No newline at end of file
index 73eb09b..492f209 100644 (file)
@@ -32,6 +32,7 @@ Texts.
 
 * Introduction::
 * Configuration::
+* Using the Bot::
 * Scripting::
 * Concept Index::
 * Function Index::
@@ -47,7 +48,7 @@ Introduction
 content.
 
 \1f
-File: bobot++.info,  Node: Configuration,  Next: Scripting,  Prev: Introduction,  Up: Top
+File: bobot++.info,  Node: Configuration,  Next: Using the Bot,  Prev: Introduction,  Up: Top
 
 Configuration
 *************
@@ -82,7 +83,39 @@ or you want to have your own personal configration, put it in
 `~/.bobotpp/config/default/'.
 
 \1f
-File: bobot++.info,  Node: Scripting,  Next: Concept Index,  Prev: Configuration,  Up: Top
+File: bobot++.info,  Node: Using the Bot,  Next: Scripting,  Prev: Configuration,  Up: Top
+
+Using Bobot++
+*************
+
+   FIXME: stuff here...
+
+* Menu:
+
+* User Levels::
+
+\1f
+File: bobot++.info,  Node: User Levels,  Prev: Using the Bot,  Up: Using the Bot
+
+User Levels
+===========
+
+   There are five levels that a user may be when interfacing with a bot:
+NONE, USER, TRUSTED_USER, FRIEND, MASTER. All users default to NONE
+unless they are changed by a script, the `!adduser' command or the
+`bot.users' file. NONE is for everyone--very few commands (e.g. help)
+are available to the users and almost everyone should be this level. A
+USER can execute many of the bot commands, but can't use masks on kicks
+and bans. A TRUSTED user can everything a USER can do, but can also use
+masks on kicks and bans. A FRIEND can do everything except for stopping
+the bot (be careful who you give this to!). The MASTER level is for the
+bot's owner (probably you) and can do _everything_ to the bot. Be
+_very_ careful if you give MASTER level access to anyone else. You
+cannot use this symbolic levels with the `!adduser' command. See
+(FIXME: ref) for the numbers you must use with `!adduser'.
+
+\1f
+File: bobot++.info,  Node: Scripting,  Next: Concept Index,  Prev: Using the Bot,  Up: Top
 
 Scripting
 *********
@@ -104,6 +137,8 @@ should be enough to convert your code to use the new functions.
 
 * Adding New Commands::
 * Hooks::
+* Scheme User Levels::
+* Sending Messages::
 
 \1f
 File: bobot++.info,  Node: Adding New Commands,  Next: Hooks,  Prev: Scripting,  Up: Scripting
@@ -111,10 +146,29 @@ File: bobot++.info,  Node: Adding New Commands,  Next: Hooks,  Prev: Scripting,
 Adding New Commands
 ===================
 
-   Not here yet.
+   Adding a new command is simple. To register a new command use
+`bot:addcommand'. The prototype for `bot:addcommand' is
+`(bot:addcommand name func needs-channel? num-of-args min-level)'. The
+`name' is a string representing the name of the command being added.
+`func' is a function accepting `num-of-args' arguments.
+`needs-channel?' is a bool that is true if the function needs the
+channel name as its first arg, and false otherwise. `num-of-args' is
+the number of args `func' will take and must be within zero (0) and
+twenty (20). `min-level' is one of the *Note Scheme User Levels::. A
+user must be at least a `min-level' user to use the new command. None
+of the arguments are guaranteed to be passed; if they aren't they are
+set to the empty string `""'. An example of a new command would be:
+(define (hello channel name)   (if (string=? name "")}@*     (bot:say
+channel "Hello world!")      (bot:say channel (string-append "Hello "
+name "!")))
+
+   (bot:addcommand "hello" hello #t 2 0)
+
+   This will display ``Hello World!'' if called as !hello and ``Hello
+World `USER''' if called as !hello USER.
 
 \1f
-File: bobot++.info,  Node: Hooks,  Prev: Adding New Commands,  Up: Scripting
+File: bobot++.info,  Node: Hooks,  Next: Scheme User Levels,  Prev: Adding New Commands,  Up: Scripting
 
 Hooks
 =====
@@ -148,7 +202,7 @@ specifies the type of hook (the types of hooks are listed in *Note Hook
 Types::). `regex' is a standard regular expression. If `regex' is
 matched, `function' will be called. `function' will take a different
 number of args depending on the hook type. `pri' specifies the priority
-of the hook-higher priority hooks are executed first. This argument is
+of the hook---higher priority hooks are executed first. This argument is
 optional and defaults to `0'. `fall' is `#t' if the hook is a
 fallthrough hook and `#f' is the hook is not a fallthrough hook. This
 arg is also optional and default to `#t'.
@@ -174,7 +228,7 @@ general format of a hook is:
 
              - `argN': desc
 
-   That said, here is the list of available hooks:
+   That said, here is the list of available hooks: FIXME: write docs
 
    * `hooks/action'
         - Description of the hook
@@ -182,126 +236,108 @@ general format of a hook is:
         - # of args
              - `arg1': desc
 
-
    * `hooks/nickname'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/signoff'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/ctcp'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/ctcp-reply'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/disconnect'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/flood'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/invite'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/join'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/kick'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/part'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/mode'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/message'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/notice'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/public'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/public-notice'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/raw'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/timer'
         - Description of the hook
 
         - # of args
              - `arg1': desc
 
-
    * `hooks/topic'
         - Description of the hook
 
@@ -309,6 +345,73 @@ general format of a hook is:
              - `arg1': desc
 
 
+\1f
+File: bobot++.info,  Node: Scheme User Levels,  Next: Sending Messages,  Prev: Hooks,  Up: Scripting
+
+Scheme User Levels
+==================
+
+   There are five levels that a user may be when interfacing with a bot:
+NONE, USER, TRUSTED_USER, FRIEND, MASTER. The Scheme variables for the
+user levels are `bot:user-none', `bot:user-user', `bot:user-trusted',
+`bot:user-friend', and `bot:user-master'. See *Note User Levels:: for
+more information on User Levels.
+
+   When adding a new command, think about who should be able to use it.
+Is your command a general purpose command that helps the channel (e.g.
+`!seen') that everyone should be able to use? Or is it something that
+should be restricted? See *Note User Levels:: for information on what
+level users can do what with the built in bot commands and think about
+what level a user your command is targetted towards. You must be _very_
+careful when giving new commands to lower level users because you can
+do basically everything the bot can do with a script. As the scripting
+interface becomes more powerful, you must think more about what users
+can use new commands you add.
+
+\1f
+File: bobot++.info,  Node: Sending Messages,  Prev: Scheme User Levels,  Up: Scripting
+
+Sending Messages
+================
+
+   There are several types of messages you can send with Bobot++ from
+scripts. There is the simple, but rather limited, `bot:say',
+`bot:action' and `bot:msg', and the more powerful, but lower level,
+`bot:send-MESSAGE' functions. Most bots will probably only need the
+higher level functions, but for the sake of why-not Bobot++ lets you
+use the lower level functions.
+
+* Menu:
+
+* High Level Message Functions::
+* Low Level Message Functions::
+
+\1f
+File: bobot++.info,  Node: High Level Message Functions,  Next: Low Level Message Functions,  Prev: Sending Messages,  Up: Sending Messages
+
+``High Level'' Message Functions
+--------------------------------
+
+   ...
+
+\1f
+File: bobot++.info,  Node: Low Level Message Functions,  Prev: High Level Message Functions,  Up: Sending Messages
+
+``Low Level'' Message Functions
+-------------------------------
+
+   The ``Low Level'' messaging functions allow you to do things like
+send CTCP messages. You probably want to read rfc 2812 and the CTCP spec
+before using these. If you have no idea what these do, read rfc 2812
+(IRC Client Protocol) and CTCP spec. These functions all return
+`*unspecified*' always, so don't use the return value for anything.
+
+   * `bot:send-CTCP to command message' `to' is the target of your CTCP
+     message, `command' is the CTCP command, and `message' is the
+     message (or arguments) of the command. Make sure to
+     `bot:ctcp-quote' the message!
+
+
 \1f
 File: bobot++.info,  Node: Concept Index,  Next: Function Index,  Prev: Scripting,  Up: Top
 
@@ -316,6 +419,9 @@ Concept Index
 *************
 
 * Menu:
+
+* Background on Hooks:                   Hooks.
+
 \1f
 File: bobot++.info,  Node: Function Index,  Next: Variable Index,  Prev: Concept Index,  Up: Top
 
@@ -323,6 +429,10 @@ Function Index
 **************
 
 * Menu:
+
+* addcommand:                            Adding New Commands.
+* addhook:                               Creating a Hook.
+
 \1f
 File: bobot++.info,  Node: Variable Index,  Prev: Function Index,  Up: Top
 
@@ -331,20 +441,52 @@ Variable Index
 
 * Menu:
 
+* hooks/action:                          Hook Types.
+* hooks/ctcp:                            Hook Types.
+* hooks/ctcp-reply:                      Hook Types.
+* hooks/disconnect:                      Hook Types.
+* hooks/flood:                           Hook Types.
+* hooks/invite:                          Hook Types.
+* hooks/join:                            Hook Types.
+* hooks/kick:                            Hook Types.
+* hooks/message:                         Hook Types.
+* hooks/mode:                            Hook Types.
+* hooks/nickname:                        Hook Types.
+* hooks/notice:                          Hook Types.
+* hooks/part:                            Hook Types.
+* hooks/public:                          Hook Types.
+* hooks/public-notice:                   Hook Types.
+* hooks/raw:                             Hook Types.
+* hooks/signoff:                         Hook Types.
+* hooks/timer:                           Hook Types.
+* hooks/topic:                           Hook Types.
+* user-friend:                           Scheme User Levels.
+* user-master:                           Scheme User Levels.
+* user-none:                             Scheme User Levels.
+* user-trusted:                          Scheme User Levels.
+* user-user:                             Scheme User Levels.
+
+
 \1f
 Tag Table:
 Node: Top\7f517
-Node: Introduction\7f1228
-Node: Configuration\7f1419
-Node: Configuration File Syntax\7f1801
-Node: Configure File Placement\7f2003
-Node: Scripting\7f2477
-Node: Adding New Commands\7f3316
-Node: Hooks\7f3473
-Node: Creating a Hook\7f4363
-Node: Hook Types\7f5153
-Node: Concept Index\7f7626
-Node: Function Index\7f7758
-Node: Variable Index\7f7897
+Node: Introduction\7f1246
+Node: Configuration\7f1437
+Node: Configuration File Syntax\7f1823
+Node: Configure File Placement\7f2025
+Node: Using the Bot\7f2499
+Node: User Levels\7f2673
+Node: Scripting\7f3677
+Node: Adding New Commands\7f4560
+Node: Hooks\7f5803
+Node: Creating a Hook\7f6720
+Node: Hook Types\7f7512
+Node: Scheme User Levels\7f9985
+Node: Sending Messages\7f11114
+Node: High Level Message Functions\7f11682
+Node: Low Level Message Functions\7f11900
+Node: Concept Index\7f12659
+Node: Function Index\7f12841
+Node: Variable Index\7f13102
 \1f
 End Tag Table
index f32b98c..16cf46f 100644 (file)
@@ -57,6 +57,7 @@ Texts.
 @menu
 * Introduction::                
 * Configuration::               
+* Using the Bot::               
 * Scripting::                   
 * Concept Index::               
 * Function Index::              
@@ -69,7 +70,7 @@ Texts.
 This manual feels abused and neglected because it has almost no
 content.
 
-@node Configuration, Scripting, Introduction, Top
+@node Configuration, Using the Bot, Introduction, Top
 @chapter Configuration
 
 Bobot++ is easy to configure. The configuration file format may be
@@ -95,7 +96,36 @@ files you want to be loaded by default in this directory. If you are
 not root or you want to have your own personal configration, put it in
 @file{~/.bobotpp/config/default/}. 
 
-@node Scripting, Concept Index, Configuration, Top
+@node Using the Bot, Scripting, Configuration, Top
+@chapter Using Bobot++
+
+FIXME: stuff here...
+
+@menu
+* User Levels::                 
+@end menu
+
+@node User Levels,  , Using the Bot, Using the Bot
+@section User Levels
+
+There are five levels that a user may be when interfacing with a bot:
+@var{none}, @var{user}, @var{trusted_user}, @var{friend},
+@var{master}. All users default to @var{none} unless they are changed
+by a script, the @code{!adduser} command or the @file{bot.users}
+file. @var{none} is for everyone---very few commands (e.g. help) are
+available to the users and almost everyone should be this
+level. A @var{user} can execute many of the bot commands, but can't
+use masks on kicks and bans. A @var{trusted} user can everything a
+@var{user} can do, but can also use masks on kicks and bans. A
+@var{friend} can do everything except for stopping the bot (be
+careful who you give this to!). The @var{master} level is for the
+bot's owner (probably you) and can do @emph{everything} to the bot. Be
+@emph{very} careful if you give @var{master} level access to anyone
+else. You cannot use this symbolic levels with the @code{!adduser}
+command. See (FIXME: ref) for the numbers you must use with
+@code{!adduser}.
+
+@node Scripting, Concept Index, Using the Bot, Top
 @chapter Scripting
 
 Bobot++'s most powerful feature is its scripting system. You write
@@ -115,16 +145,42 @@ convert your code to use the new functions.
 @menu
 * Adding New Commands::         
 * Hooks::                       
+* Scheme User Levels::          
+* Sending Messages::            
 @end menu
 
 @node Adding New Commands, Hooks, Scripting, Scripting
 @section Adding New Commands
 
-Not here yet.
-
-@node Hooks,  , Adding New Commands, Scripting
+@findex addcommand
+Adding a new command is simple. To register a new command use
+@code{bot:addcommand}. The prototype for @code{bot:addcommand} is
+@code{(bot:addcommand name func needs-channel? num-of-args
+min-level)}. The @code{name} is a string representing the name of the
+command being added. @code{func} is a function accepting
+@code{num-of-args} arguments. @code{needs-channel?} is a bool that is
+true if the function needs the channel name as its first arg, and
+false otherwise. @code{num-of-args} is the number of args @code{func}
+will take and must be within zero (0) and twenty
+(20). @code{min-level} is one of the @ref{Scheme User Levels}. A user must be
+at least a @code{min-level} user to use the new command. None of the
+arguments are guaranteed to be passed; if they aren't they are set to
+the empty string @code{""}. An example of a new command would be:
+@verb{|
+(define (hello channel name)
+  (if (string=? name "")}@*
+    (bot:say channel "Hello world!")
+    (bot:say channel (string-append "Hello " name "!")))
+
+(bot:addcommand "hello" hello #t 2 0)
+|}
+This will display ``Hello World!'' if called as @kbd{!hello} and
+``Hello World @code{USER}'' if called as @kbd{!hello @var{USER}}. 
+
+@node Hooks, Scheme User Levels, Adding New Commands, Scripting
 @section Hooks
 
+@cindex Background on Hooks
 Hooks are a powerful feature of Bobot++. Hooks are a hybrid of ircII
 hooks and tiny fugue (a MUD bot) hooks. The basic idea of a hook if
 that you match a text against regular expression and call a function
@@ -146,6 +202,7 @@ stops.
 @node Creating a Hook, Hook Types, Hooks, Hooks
 @subsection Creating a Hook
 
+@findex addhook
 To add a new hook you use the function
 @code{bot:addhook}. @code{bot:addhook} is prototyped as
 @code{(bot:addhook type regex function pri fall)}. @code{type}
@@ -154,7 +211,7 @@ Types}). @code{regex} is a standard regular expression. If
 @code{regex} is matched, @code{function} will be
 called. @code{function} will take a different number of args depending
 on the hook type. @code{pri} specifies the priority of the
-hook--higher priority hooks are executed first. This argument is
+hook---higher priority hooks are executed first. This argument is
 optional and defaults to @code{0}. @code{fall} is @code{#t} if the
 hook is a fallthrough hook and @code{#f} is the hook is not a
 fallthrough hook. This arg is also optional and default to @code{#t}. 
@@ -187,9 +244,12 @@ Description of the hook
 @end itemize
 
 That said, here is the list of available hooks:
+FIXME: write docs
 
 @itemize @bullet
+
 @item
+@vindex hooks/action
 @code{hooks/action}
 @itemize @minus
 @item
@@ -202,9 +262,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/nickname
 @code{hooks/nickname}
 @itemize @minus
 @item
@@ -217,9 +276,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/signoff
 @code{hooks/signoff}
 @itemize @minus
 @item
@@ -232,9 +290,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/ctcp
 @code{hooks/ctcp}
 @itemize @minus
 @item
@@ -247,9 +304,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/ctcp-reply
 @code{hooks/ctcp-reply}
 @itemize @minus
 @item
@@ -262,9 +318,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/disconnect
 @code{hooks/disconnect}
 @itemize @minus
 @item
@@ -277,9 +332,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/flood
 @code{hooks/flood}
 @itemize @minus
 @item
@@ -292,9 +346,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/invite
 @code{hooks/invite}
 @itemize @minus
 @item
@@ -307,9 +360,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/join
 @code{hooks/join}
 @itemize @minus
 @item
@@ -322,9 +374,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/kick
 @code{hooks/kick}
 @itemize @minus
 @item
@@ -337,9 +388,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/part
 @code{hooks/part}
 @itemize @minus
 @item
@@ -352,9 +402,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/mode
 @code{hooks/mode}
 @itemize @minus
 @item
@@ -367,9 +416,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/message
 @code{hooks/message}
 @itemize @minus
 @item
@@ -382,9 +430,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/notice
 @code{hooks/notice}
 @itemize @minus
 @item
@@ -397,9 +444,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/public
 @code{hooks/public}
 @itemize @minus
 @item
@@ -412,9 +458,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/public-notice
 @code{hooks/public-notice}
 @itemize @minus
 @item
@@ -427,9 +472,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/raw
 @code{hooks/raw}
 @itemize @minus
 @item
@@ -442,9 +486,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/timer
 @code{hooks/timer}
 @itemize @minus
 @item
@@ -457,9 +500,8 @@ Description of the hook
 @end itemize
 @end itemize
 
-@end itemize
-@itemize @bullet
 @item
+@vindex hooks/topic
 @code{hooks/topic}
 @itemize @minus
 @item
@@ -475,6 +517,71 @@ Description of the hook
 @end itemize
 
 
+@node Scheme User Levels, Sending Messages, Hooks, Scripting
+@section Scheme User Levels
+
+@vindex user-none
+@vindex user-user
+@vindex user-trusted
+@vindex user-friend
+@vindex user-master
+There are five levels that a user may be when interfacing with a bot:
+@var{none}, @var{user}, @var{trusted_user}, @var{friend},
+@var{master}. The Scheme variables for the user levels are
+@code{bot:user-none}, @code{bot:user-user}, @code{bot:user-trusted},
+@code{bot:user-friend}, and @code{bot:user-master}. See @ref{User
+Levels} for more information on User Levels. 
+
+When adding a new command, think about who should be able to use
+it. Is your command a general purpose command that helps the channel
+(e.g. @code{!seen}) that everyone should be able to use? Or is it
+something that should be restricted? See @ref{User Levels} for
+information on what level users can do what with the built in bot
+commands and think about what level a user your command is targetted
+towards. You must be @emph{very} careful when giving new commands to
+lower level users because you can do basically everything the bot can
+do with a script. As the scripting interface becomes more powerful,
+you must think more about what users can use new commands you add.
+
+@node Sending Messages,  , Scheme User Levels, Scripting
+@section Sending Messages
+
+There are several types of messages you can send with Bobot++ from
+scripts. There is the simple, but rather limited, @code{bot:say}, 
+@code{bot:action} and @code{bot:msg}, and
+the more powerful, but lower level, @code{bot:send-MESSAGE}
+functions. Most bots will probably only need the higher level
+functions, but for the sake of why-not Bobot++ lets you use the lower
+level functions.
+
+@menu
+* High Level Message Functions::  
+* Low Level Message Functions::  
+@end menu
+
+@node High Level Message Functions, Low Level Message Functions, Sending Messages, Sending Messages
+@subsection ``High Level'' Message Functions
+
+...
+
+@node Low Level Message Functions,  , High Level Message Functions, Sending Messages
+@subsection ``Low Level'' Message Functions
+
+The ``Low Level'' messaging functions allow you to do things like send
+CTCP messages. You probably want to read rfc 2812 and the CTCP spec
+before using these. If you have no idea what these do, read rfc 2812
+(IRC Client Protocol) and CTCP spec. These functions all return
+@code{*unspecified*} always, so don't use the return value for anything.
+
+@itemize @bullet
+
+@item @code{bot:send-CTCP to command message}
+@code{to} is the target of your CTCP message, @code{command} is the
+CTCP command, and @code{message} is the message (or arguments) of the
+command. Make sure to @code{bot:ctcp-quote} the message!
+
+@end itemize
+
 @node Concept Index, Function Index, Scripting, Top
 @unnumbered Concept Index
 @printindex cp
index 133982a..3e41396 100644 (file)
@@ -40,7 +40,7 @@
 ;;; ':',',', or nothing after the bot name
 (define (match-to-me regex)
   (string-append (match-not-channel (bot:getnickname))
-                "[:,]*[[:space:][:graph:]]*" regex))
+                "[[:space:][:graph:]]*" regex))
 
 
 ;;;; string-utils
    messages )
   (bot:flushport))
 
-;;; DEPRECATED FUNCTION NAMED
+;;; Message sending utils
+
+;;; returns the CTCP quoted message
+(define (ctcp-quote message)
+  message) ; FIXME: fill me in
+
+;;; DEPRECATED FUNCTION NAMES
 ;;; These are provided for backwards compatibility
 ;;; and will be removed in the 2.3 dev tree
 
index c39b1f4..7b0827d 100644 (file)
@@ -1,8 +1,6 @@
 (define (hello c n)
-        (if (string=? n "")
-            (bot:say c "Hello world !")
-            (bot:say c (string-append "Hello " n " !"))
-        )
-)
+  (if (string=? n "")
+    (bot:say c "Hello world !")
+    (bot:say c (string-append "Hello " n " !"))))
 
 (bot:addcommand "hello" hello #t 2 0)
index 4b8c836..331b349 100644 (file)
@@ -30,6 +30,7 @@
 #include "StringTokenizer.H"
 #include "ServerConnection.H"
 #include "Utils.H"
+#include "UserCommands.H"
 
 #define DEFAULT_NICKNAME "Bobot"
 #define DEFAULT_USERNAME "bobot"
@@ -72,8 +73,6 @@ Bot::Bot(String filename, bool debug_on)
     lastNickNameChange(startTime), lastChannelJoin(startTime),
     serverConnection(0), sentUserhostID(0), receivedUserhostID(0)
 {
-  extern userFunctionsStruct userFunctionsInit[];
-
 #ifdef HAVE_STL_CLEAR
   wantedChannels.clear();
   ignoredUserhosts.clear();
@@ -81,13 +80,7 @@ Bot::Bot(String filename, bool debug_on)
   userhostMap.clear();
 #endif
 
-  for (int i = 0; userFunctionsInit[i].name[0] != '\0'; i++) {
-    userFunctions.push_back(new
-                            userFunction(String(userFunctionsInit[i].name),
-                                         userFunctionsInit[i].function,
-                                         userFunctionsInit[i].minLevel,
-                                         userFunctionsInit[i].needsChannelName));
-  }
+  init_user_functions ();
 
 #if HAVE_IOSBASE
   logFile.open(logs_dir + logFileName, std::ios_base::out | 
@@ -109,9 +102,6 @@ Bot::Bot(String filename, bool debug_on)
 
   if (initFile) {
     String temp, alias, command;
-    std::list<userFunction *>::iterator it;
-    bool found = false;
-    userFunction *u;
     int line = 0;
     while (initFile >> temp, temp.length() != 0) {
       line++;
@@ -127,32 +117,19 @@ Bot::Bot(String filename, bool debug_on)
       command = st.nextToken().toUpper();
 
       // Does the function already exist ?
-      found = false;
-      for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
-        if (alias == (*it)->name) {
-          found = true;
-          break;
-        }
-      if (found) continue;
-
-      // Check that the command exists
-      found = false;
-      for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
-        if (command == (*it)->name) {
-          found = true;
-          u = *it;
-          break;
-        }
-      if (!found) continue;
-
-      userFunctions.push_back (new
-                               userFunction((char *)(const char *)alias,
-                                           u->function,
-                                           u->minLevel,
-                                           u->needsChannelName));
+      if (!userFunctions[alias])
+       {
+         if (userFunction *u = userFunctions[command])
+           userFunctions[alias] = 
+             new
+             userFunction(u->function,
+                          u->minLevel,
+                          u->needsChannelName);
+       }
     }
   }
 
+
   std::srand (std::time (0)); // srand for bot-random
 #ifdef USESCRIPTS
   botInterp = new BotInterp(this, logs_dir + scriptLogFileName);
@@ -176,12 +153,9 @@ Bot::~Bot()
     dccConnections.erase(dccConnections.begin());
     delete d;
   }
-  userFunction *u;
-  while (userFunctions.size() != 0) {
-    u = *userFunctions.begin();
-    userFunctions.erase(userFunctions.begin());
-    delete u;
-  }
+
+  destroy_user_functions ();
+
   wantedChannel *w;
   while (wantedChannels.size() != 0) {
     w = (*wantedChannels.begin()).second;
@@ -598,3 +572,100 @@ Bot::iAmOp(String channel)
   User * me = channelList->getChannel(channel)->getUser(nickName);
   return (me->mode & User::OP_MODE);
 }
+
+void
+Bot::init_user_functions ()
+{
+    // User Functions
+#define uf(f, l, b) new userFunction (f, l, b);
+  userFunctions["ACTION"] = uf (UserCommands::Action, User::USER, true);
+  userFunctions["ADDUSER"] = uf (UserCommands::AddUser, User::FRIEND, false);
+  userFunctions["ADDSERVER"] = uf (UserCommands::AddServer, User::FRIEND,
+                                   false);
+  userFunctions["ADDSHIT"] = uf (UserCommands::AddShit, User::FRIEND, false);
+  userFunctions["ALIAS"] = uf (UserCommands::Alias, User::MASTER, false);
+  userFunctions["BAN"] = uf (UserCommands::Ban, User::USER, true);
+  userFunctions["BANLIST"] = uf (UserCommands::BanList, User::USER, true);
+  userFunctions["CHANNELS"] =
+    uf (UserCommands::Channels, User::FRIEND, false);
+  userFunctions["CYCLE"] = uf (UserCommands::Cycle, User::FRIEND, true);
+  userFunctions["DCCLIST"] = uf (UserCommands::DCCList, User::FRIEND, false);
+  userFunctions["DEBAN"] = uf (UserCommands::Deban, User::USER, true);
+  userFunctions["DELSERVER"] = uf (UserCommands::DelServer, User::FRIEND,
+                                   false);
+  userFunctions["DELUSER"] = uf (UserCommands::DelUser, User::FRIEND, false);
+  userFunctions["DELSHIT"] = uf (UserCommands::DelShit, User::FRIEND, false);
+  userFunctions["DEOP"] = uf (UserCommands::Deop, User::TRUSTED_USER, true);
+  userFunctions["DIE"] = uf (UserCommands::Die, User::MASTER, false);
+  userFunctions["DO"] = uf (UserCommands::Do, User::MASTER, false);
+#ifdef USESCRIPTS
+  userFunctions["EXECUTE"] = uf (UserCommands::Execute, User::MASTER, false);
+#endif
+  userFunctions["HELP"] = uf (UserCommands::Help, User::NONE, false);
+  userFunctions["IDENT"] = uf (UserCommands::Ident, User::NONE, true);
+  userFunctions["INVITE"] = uf (UserCommands::Invite, User::USER, true);
+  userFunctions["JOIN"] = uf (UserCommands::Join, User::FRIEND, false);
+  userFunctions["KEEP"] = uf (UserCommands::Keep, User::FRIEND, true);
+  userFunctions["KICK"] = uf (UserCommands::Kick, User::USER, true);
+  userFunctions["KICKBAN"] = uf (UserCommands::KickBan, User::USER, true);
+  userFunctions["LOAD"] = uf (UserCommands::Load, User::FRIEND, false);
+#ifdef USESCRIPTS
+  userFunctions["LOADSCRIPT"] = uf (UserCommands::LoadScript, User::MASTER,
+                                    false);
+#endif
+  userFunctions["LOCK"] = uf (UserCommands::Lock, User::FRIEND, true);
+  userFunctions["MODE"] = uf (UserCommands::Mode, User::FRIEND, true);
+  userFunctions["MSG"] = uf (UserCommands::Msg, User::USER, false);
+  userFunctions["NAMES"] = uf (UserCommands::Names, User::USER, true);
+  userFunctions["NEXTSERVER"] = uf (UserCommands::NextServer, User::FRIEND,
+                                    false);
+  userFunctions["NICK"] = uf (UserCommands::Nick, User::FRIEND, false);
+  userFunctions["NSLOOKUP"] = uf (UserCommands::NsLookup, User::USER, false);
+  userFunctions["OP"] = uf (UserCommands::Op, User::TRUSTED_USER, true);
+  userFunctions["PART"] = uf (UserCommands::Part, User::FRIEND, true);
+  userFunctions["PASSWORD"] = uf (UserCommands::Password, User::USER, true);
+  userFunctions["RECONNECT"] =
+    uf (UserCommands::Reconnect, User::FRIEND, false);
+  userFunctions["RSPYMESSAGE"] =
+    uf (UserCommands::RSpyMessage, User::USER, false);
+  userFunctions["SAVE"] = uf (UserCommands::Save, User::FRIEND, false);
+  userFunctions["SAY"] = uf (UserCommands::Say, User::USER, true);
+  userFunctions["SERVER"] = uf (UserCommands::Server, User::FRIEND, false);
+  userFunctions["SERVERLIST"] =
+    uf (UserCommands::ServerList, User::FRIEND, false);
+  userFunctions["SETVERSION"] =
+    uf (UserCommands::SetVersion, User::MASTER, false);
+  userFunctions["SHITLIST"] =
+    uf (UserCommands::ShitList, User::FRIEND, false);
+  userFunctions["SPYLIST"] = uf (UserCommands::SpyList, User::USER, false);
+  userFunctions["SPYMESSAGE"] =
+    uf (UserCommands::SpyMessage, User::USER, false);
+  userFunctions["STATS"] = uf (UserCommands::Stats, User::FRIEND, true);
+  userFunctions["TBAN"] = uf (UserCommands::TBan, User::USER, true);
+  userFunctions["TKBAN"] = uf (UserCommands::TKBan, User::USER, true);
+  userFunctions["TOPIC"] = uf (UserCommands::Topic, User::USER, true);
+  userFunctions["UNLOCK"] = uf (UserCommands::Unlock, User::FRIEND, true);
+  userFunctions["USERLIST"] =
+    uf (UserCommands::UserList, User::FRIEND, false);
+  userFunctions["WHO"] = uf (UserCommands::Who, User::NONE, true);
+  userFunctions["WHOIS"] = uf (UserCommands::Whois, User::FRIEND, true);
+#undef uf
+}
+
+namespace 
+{
+  void erase_userf (std::pair<std::string, class userFunction*> it)
+  {
+    delete it.second;
+  }
+}
+
+void
+Bot::destroy_user_functions ()
+{
+  for_each (userFunctions.begin (),
+           userFunctions.end (),
+           erase_userf);
+  userFunctions.erase (userFunctions.begin (),
+                      userFunctions.end ());
+}
index 67bf12b..a0bbcd0 100644 (file)
@@ -26,6 +26,8 @@
 #include <ctime>
 #include <set>
 #include <fstream>
+#include <string>
+#include <map>
 
 #include "String.H"
 #include "Person.H"
@@ -87,8 +89,9 @@ public:
 #ifdef USESCRIPTS
   BotInterp * botInterp;
 #endif
-  std::list<class userFunction *> userFunctions;
-
+  //  std::list<class userFunction *> userFunctions;
+  std::map<std::string, class userFunction*, 
+          std::less<std::string> > userFunctions;
   std::map<String, wantedChannel *, std::less<String> > wantedChannels;
 
   std::map<String, unsigned int, std::less<String> > ignoredUserhosts;
@@ -154,6 +157,9 @@ private:
   void rehash();
   String getUserhost(String, String);
   bool iAmOp(String);
+
+  void init_user_functions ();
+  void destroy_user_functions ();
 };
 
 #endif
index f4de9ee..cba72d0 100644 (file)
@@ -151,6 +151,11 @@ Interp::Startup()
                     2, 0, 0);
   bot_new_procedure ("bot:deltimer", (SCMFunc)ScriptCommands::DelTimer, 
                     1, 0, 0);
+
+  // "Low Level" Message functuions
+  scm_c_define_gsubr ("bot:send-CTCP", 3, 0, 0,
+                     (SCMFunc)ScriptCommands::sendCTCP);
+
   // load bobot-utils
   scm_primitive_load 
     (scm_makfrom0str (String(PREFIX) + 
dissimilarity index 88%
index 7d64cab..257f26c 100644 (file)
-// Parser.C  -*- C++ -*-
-// Copyright (c) 1997, 1998 Etienne BERNARD
-// Copyright (C) 2002 Clinton Ebadi
-
-// 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
-// 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.
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include "StringTokenizer.H"
-#include "Parser.H"
-#include "UserCommands.H"
-#include "Macros.H"
-#include "Utils.H"
-#include "ShitList.H"
-
-typedef void (*fptr)(ServerConnection *, Person *, String);
-std::map<std::string, fptr, std::less<std::string> > Parser::functions;
-
-void Parser::init ()
-{
-  Parser::functions["001"] = Parser::parse001; /* RPL_WELCOME */
-  Parser::functions["302"] = Parser::parse302; /* RPL_USERHOST */
-  Parser::functions["311"] = Parser::parse311; /* RPL_WHOISUSER */
-  Parser::functions["315"] = Parser::parse315; /* RPL_ENDOFWHO */
-  Parser::functions["324"] = Parser::parse324; /* RPL_CHANNELMODEIS */
-  Parser::functions["332"] = Parser::parse332; /* RPL_TOPIC */
-  Parser::functions["352"] = Parser::parse352; /* RPL_WHOREPLY */
-  Parser::functions["353"] = Parser::parse353; /* RPL_NAMESREPLY */
-  Parser::functions["366"] = Parser::parse366; /* RPL_ENDOFNAMES */
-  Parser::functions["367"] = Parser::parse367; /* RPL_BANLIST */
-  Parser::functions["401"] = Parser::parse401; /* ERR_NOSUCHNICK */
-  Parser::functions["433"] = Parser::parse433; /* ERR_NICKNAMEINUSE */
-  Parser::functions["437"] = Parser::parse433; /* ERR_UNAVAILRESOURCE */
-  Parser::functions["471"] = Parser::parse473; /* ERR_CHANNELISFULL */
-  Parser::functions["473"] = Parser::parse473; /* ERR_INVITEONLYCHAN */
-  Parser::functions["474"] = Parser::parse473; /* ERR_BANNEDFROMCHAN */
-  Parser::functions["475"] = Parser::parse473; /* ERR_BADCHANNELKEY */
-  Parser::functions["ERROR"] = Parser::parseError;
-  Parser::functions["INVITE"] = Parser::parseInvite;
-  Parser::functions["JOIN"] = Parser::parseJoin;
-  Parser::functions["KICK"] = Parser::parseKick ;
-  Parser::functions["MODE"] = Parser::parseMode ;
-  Parser::functions["NICK"] = Parser::parseNick ;
-  Parser::functions["NOTICE"] = Parser::parseNotice;
-  Parser::functions["PART"] = Parser::parsePart;
-  Parser::functions["PING"] = Parser::parsePing;
-  Parser::functions["PONG"] = Parser::parsePong;
-  Parser::functions["PRIVMSG"] = Parser::parsePrivmsg;
-  Parser::functions["QUIT"] = Parser::parseQuit;
-  Parser::functions["TOPIC"] = Parser::parseTopic;
-  Parser::functions[""] = Parser::parseError;
-}
-
-
-void
-Parser::parseLine(ServerConnection * cnx, String line)
-{
-  StringTokenizer st(line);
-  Person * from = 0;
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::RAW, line,
-                   scm_listify (Utils::string2SCM(line),
-                           SCM_UNDEFINED));
-#endif
-
-  if (line[0] == ':') {
-    String fromMask = st.nextToken().subString(1);
-    if (fromMask.find('!') != -1)
-      from = new Person(cnx->bot, fromMask);
-  }
-
-  String command = st.nextToken();
-  String rest = st.rest();
-
-  /*  for (int i = 0; functions[i].name != 0; i++)
-    if (command == functions[i].name) {
-      functions[i].function(cnx, from, rest);
-      break;
-    }
-  */
-  if (fptr temp_func = functions[command])
-    temp_func (cnx, from, rest);
-
-  delete from;
-}
-
-void
-Parser::parse001(ServerConnection * cnx,
-                 Person *from, String rest)
-{
-  String temp = "";
-  StringTokenizer st(rest);
-  String realNick = st.nextToken();
-
-  if ((cnx->bot->nickName).toLower() != realNick) {
-    // Yes, this can happen, and it was a very subtle bug
-    cnx->bot->nickName = realNick;
-    cnx->bot->userList->removeFirst();
-    cnx->bot->userList->addUserFirst(realNick + "!" + cnx->bot->userHost, "*", 0, 3, true, -1, "");
-    cnx->bot->lastNickNameChange = time(0);
-    cnx->bot->rehash();
-  }
-
-  cnx->bot->connected = true;
-
-  cnx->queue->sendUserMode(cnx->bot->nickName, "+i");
-  cnx->queue->sendWhois(cnx->bot->nickName);
-
-  for (std::map<String, wantedChannel *, std::less<String> >::iterator
-         it = cnx->bot->wantedChannels.begin();
-       it != cnx->bot->wantedChannels.end(); ++it)
-    cnx->queue->sendJoin((*it).first, (*it).second->key);
-
-  cnx->bot->logLine(String("Connected to server ") +
-               cnx->bot->serverList->currentServer()->getHostName() +
-               " (" +  String((long)cnx->bot->serverList->currentServer()->getPort()) +
-               ").");
-}
-
-void
-Parser::parse302(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  unsigned long num = cnx->bot->receivedUserhostID++;
-  StringTokenizer st(rest);
-
-  st.nextToken(':');
-
-  if (st.rest().length()) {
-    st.nextToken('=');
-    String parameters = st.rest();
-    parameters = parameters.subString(1);
-    cnx->bot->userhostMap[num] = parameters;
-  } else
-    cnx->bot->userhostMap[num] = "";
-}
-
-void
-Parser::parse311(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String nuh = st.nextToken() + "!";
-  String uh = st.nextToken() + "@";
-  uh = uh + st.nextToken();
-  nuh = nuh + uh;
-  cnx->bot->userList->addUserFirst(nuh, "*", 0, 3, true, -1, "");
-  cnx->bot->userHost = uh;
-}
-
-void
-Parser::parse315(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String channel = st.nextToken();
-  Channel *c = cnx->bot->channelList->getChannel(channel);
-  if (!c)
-    return;
-  c->gotWho = true;
-}
-
-void
-Parser::parse324(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String channel = st.nextToken();
-  if (Channel *c = cnx->bot->channelList->getChannel(channel))
-    if (c) c->parseMode(from, st.rest());
-}
-
-void
-Parser::parse332(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String channel = st.nextToken();
-  if (Channel *c = cnx->bot->channelList->getChannel(channel))
-    if (c) c->channelTopic = st.rest().subString(1);
-}
-
-void
-Parser::parse352(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String ch = st.nextToken();
-  String uh = st.nextToken() + "@";
-  uh = uh + st.nextToken();
-  st.nextToken();
-  String n = st.nextToken();
-  String m = st.nextToken();
-  int mode = 0;
-  
-  for (int i = 0; i < m.length(); i++)
-    switch (m[i]) {
-    case 'H': break;
-    case 'G': mode |= User::AWAY_MODE; break;
-    case '*': mode |= User::IRCOP_MODE; break;
-    case '@': mode |= User::OP_MODE; break;
-    case '+': mode |= User::VOICE_MODE; break;
-    }
-  if (Channel *c = cnx->bot->channelList->getChannel(ch))
-    if (c) c->addNick(n, uh, mode, cnx->bot->userList);
-}
-
-void
-Parser::parse353(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  int mode = 0;
-  String nick;
-
-  StringTokenizer st(rest);
-  st.nextToken(); st.nextToken();
-
-  Channel * c = cnx->bot->channelList->getChannel(st.nextToken());
-  if (!c) return;
-  StringTokenizer st2(st.nextToken(':'));
-
-  while (st2.hasMoreTokens()) {
-    nick = st2.nextToken();
-    if (nick[0] == '@') {
-      mode = User::OP_MODE;
-      nick = nick.subString(1);
-    } else if (nick[0] == '+') {
-      mode = User::VOICE_MODE;
-      nick = nick.subString(1);
-    }
-    c->addNick(nick, "", mode, 0, true);
-  }
-}
-
-void
-Parser::parse366(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String ch = st.nextToken();
-  if (Channel *c = cnx->bot->channelList->getChannel(ch))
-    c->joined = true;
-}
-
-void
-Parser::parse367(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String ch = st.nextToken();
-  if (Channel *c = cnx->bot->channelList->getChannel(ch))
-    c->addBan(st.nextToken(), -1);
-}
-
-void
-Parser::parse401(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-  String nick = st.nextToken();
-
-  if (cnx->bot->spyList.find(nick) != cnx->bot->spyList.end()) {
-    delete cnx->bot->spyList[nick];
-    cnx->bot->spyList.erase(nick);
-  }
-}
-
-void
-Parser::parse433(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  if (cnx->bot->connected)
-    return;
-
-  if (cnx->bot->nickName.length() == 9) {
-    int i;
-    for (i = 0; i < cnx->bot->nickName.length() && cnx->bot->nickName[i] == '_'; i++)
-       ;
-     if (i < cnx->bot->nickName.length())
-       cnx->bot->nickName = cnx->bot->nickName.subString(0, i-1) + "_" + cnx->bot->nickName.subString(i+1);
-     else
-       cnx->bot->nickName = cnx->bot->nickName.subString(0, 4) +
-         String((long)(rand() % 10000));
-   }
-   else
-     cnx->bot->nickName = cnx->bot->nickName + "_";
-
-  cnx->queue->sendNick(cnx->bot->nickName);
-}
-
-void
-Parser::parse473(ServerConnection *cnx,
-                 Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  st.nextToken();
-
-  cnx->bot->logLine(String("Unable to join channel ") +
-               st.nextToken() + ".");
-}
-
-void
-Parser::parseError(ServerConnection *cnx,
-                   Person *from, String rest)
-{
-  cnx->bot->logLine(String("Error from server ") +
-               cnx->bot->serverList->currentServer()->getHostName() +
-               " (" + String((long)cnx->bot->serverList->currentServer()->getPort()) +
-               ").");
-  cnx->bot->nextServer();
-}
-
-void
-Parser::parseInvite(ServerConnection *cnx,
-                    Person *from, String rest)
-{
-  String nick = from->getNick();
-  StringTokenizer st(rest);
-  st.nextToken(':');
-  String channel = st.rest();
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::INVITE, nick + " " + channel,
-                   scm_listify (Utils::string2SCM(nick),
-                           Utils::string2SCM(channel),             
-                           SCM_UNDEFINED));
-#endif
-
-  if (cnx->bot->wantedChannels.find(channel) !=
-      cnx->bot->wantedChannels.end())
-    cnx->queue->sendJoin(channel, cnx->bot->wantedChannels[channel]->key);
-}
-
-void
-Parser::parseJoin(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  StringTokenizer st(from->getAddress());
-  String n = st.nextToken('!');
-  String uh = st.nextToken();
-  StringTokenizer st2(rest);
-  String c = st2.nextToken(':');
-  String mode;
-  bool joinAndMode = false;
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::JOIN, n + " " + c,
-                   scm_listify (Utils::string2SCM(n),
-                           Utils::string2SCM(c),             
-                           SCM_UNDEFINED));
-#endif
-
-  // This part of code is for the combined JOIN & MODE of ircd 2.9
-  if (c.find('\007') >= 0) {
-    joinAndMode = true;
-    StringTokenizer st3(c);
-    c = st3.nextToken('\007');
-    String m = st3.rest();
-    mode = c + " +" + m;
-    for (int i = 0; i < m.length(); i++)
-      mode = mode + " " + n;    
-  }
-
-  if (n == cnx->bot->nickName) {
-    cnx->bot->logLine(String("Joined channel ") + c + ".");
-    if (cnx->bot->wantedChannels.find(c) != cnx->bot->wantedChannels.end())
-      cnx->bot->channelList->addChannel(cnx, c, cnx->bot->wantedChannels[c]->keep);
-    else
-      cnx->bot->channelList->addChannel(cnx, c);
-    cnx->queue->sendWho(c);
-    cnx->queue->sendChannelMode(String("MODE ") + c + " b");
-    cnx->queue->sendChannelMode(String("MODE ") + c);
-  } else {
-    Channel * ch = cnx->bot->channelList->getChannel(c);
-    if (!ch)
-      return;
-    ShitEntry * se = cnx->bot->shitList->getShit(n+"!"+uh, c);
-    if (se && se->isStillValid() &&
-        se->getShitLevel() >= ShitEntry::SHIT_NOJOIN) {
-      cnx->queue->sendChannelMode(c, "+b", se->getMask());
-      cnx->queue->sendKick(c, n, se->getShitReason());
-      return;
-    }
-    ch->addNick(n, uh, 0, cnx->bot->userList);
-    if (ch->getUser(n)->getAop() && !(ch->getUser(n)->mode & User::OP_MODE) && cnx->bot->iAmOp(c)) {
-      // This is a part of the antispoof code
-      ch->getUser(n)->userkey = Utils::getKey();
-      cnx->queue->sendCTCP(n, "PING", ch->getUser(n)->userkey + " " + c);
-    }
-  }
-
-  if (joinAndMode)
-    parseMode(cnx, 0, mode);
-}
-
-void
-Parser::parseKick(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  String channel = st.nextToken();
-  String target = st.nextToken();
-  String reason = st.rest().subString(1);
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::KICK, target + " " + from->getNick() + " "
-                   + channel + " " + reason,
-                   scm_listify (Utils::string2SCM(target),
-                           Utils::string2SCM(from->getNick()),
-                           Utils::string2SCM(channel),
-                           Utils::string2SCM(reason),
-                           SCM_UNDEFINED));
-#endif
-
-  if (target == cnx->bot->nickName) {
-    cnx->bot->logLine(from->getAddress() + " kicked me out of channel " +
-                 channel + " (" + reason + ").");
-    cnx->queue->sendJoin(channel, cnx->bot->channelList->getChannel(channel)->channelKey);
-    cnx->bot->channelList->delChannel(channel);
-  } else {
-    if (!cnx->bot->channelList->getChannel(channel)) return;
-    User *u = cnx->bot->channelList->getChannel(channel)->getUser(target);
-    if (u && u->getProt() >= User::NO_KICK) {
-      String fromNick = from->getNick();
-      User *v = cnx->bot->channelList->getChannel(channel)->getUser(fromNick);
-      if (v->getProt() < User::NO_KICK) {
-        cnx->bot->logLine(from->getAddress() + " kicked " + target +
-                     " (protected) out of channel " + channel +
-                     " (" + reason + ").");
-        cnx->queue->sendKick(channel, fromNick,
-                        target + " \002is protected !\002");
-      }
-    }
-    cnx->bot->channelList->getChannel(channel)->delNick(target);
-  }
-}
-
-void
-Parser::parseMode(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-   StringTokenizer st(rest);
-   String ch = st.nextToken();
-   String modes = st.rest();
-
-#ifdef USESCRIPTS
-   if (from)
-     cnx->bot->botInterp->RunHooks(Hook::MODE, from->getNick() + " " + ch +
-                      " " + modes,
-                      scm_listify (Utils::string2SCM(from->getNick()),
-                              Utils::string2SCM(ch),
-                              Utils::string2SCM(modes),
-                              SCM_UNDEFINED));
-#endif
-
-
-   if (Utils::isChannel(ch)) {
-     Channel *c = cnx->bot->channelList->getChannel(ch);
-     if (!c)
-       return;
-     if (from)
-       c->parseMode(from, modes);
-     else
-       c->parseMode(0, modes);
-   }       
-}
-
-void
-Parser::parseNick(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  String on_orig = from->getNick();
-  String on = on_orig.toLower();
-  String nn = rest.subString(1);
-  String nn_lower = nn.toLower();
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::NICKNAME, on_orig + " " + nn,
-                   scm_listify (Utils::string2SCM(on_orig),
-                           Utils::string2SCM(nn),
-                           SCM_UNDEFINED));
-#endif
-
-  if ((cnx->bot->nickName).toLower() == on) {
-    cnx->bot->userList->removeFirst();
-    cnx->bot->userList->addUserFirst(nn + "!" + cnx->bot->userHost, "*", 0, 3, true, -1, "");
-    cnx->bot->lastNickNameChange = time(0);
-    cnx->bot->nickName = nn;
-    cnx->bot->rehash();
-  }
-
-  if (cnx->bot->spyList.find(on) != cnx->bot->spyList.end()) {
-    cnx->bot->spyList[nn_lower] = cnx->bot->spyList[on];
-    cnx->bot->spyList.erase(on);
-  }
-
-  for (std::map<String, Channel *, std::less<String> >::iterator it =
-         cnx->bot->channelList->begin();
-       it != cnx->bot->channelList->end();
-       ++it)
-    if ((*it).second->hasNick(on))
-      (*it).second->changeNick(on, nn_lower);
-}
-
-void
-Parser::parseNotice(ServerConnection *cnx,
-                    Person *from, String rest)
-{
-  String nick = "";
-
-  if (from)
-    nick = from->getNick();
-
-  StringTokenizer st(rest);
-  String to = st.nextToken();
-
-  rest = st.rest().subString(1);
-
-  if (rest[0] != '\001') {
-#ifdef USESCRIPTS
-    if (Utils::isChannel(to))
-      cnx->bot->botInterp->RunHooks(Hook::PUBLIC_NOTICE, nick + " " +
-                       to + " " + rest,
-                       scm_listify (Utils::string2SCM(nick),
-                               Utils::string2SCM(to),
-                               Utils::string2SCM(rest),
-                               SCM_UNDEFINED));
-    else
-      cnx->bot->botInterp->RunHooks(Hook::NOTICE, nick + " " + rest,
-                       scm_listify (Utils::string2SCM(nick),
-                               Utils::string2SCM(rest),
-                               SCM_UNDEFINED));
-#endif
-    return;
-  }
-
-  rest = rest.subString(1, rest.length() - 2);
-  StringTokenizer st2(rest);
-  String command = st2.nextToken();
-  rest = st2.rest();
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::CTCP_REPLY, nick + " " + command +
-                   " " + rest,
-                   scm_listify (Utils::string2SCM(nick),
-                           Utils::string2SCM(command),
-                           Utils::string2SCM(rest),
-                           SCM_UNDEFINED));
-#endif
-
-  if (command == "PING") {
-    StringTokenizer st3(rest);
-    rest = st3.nextToken();
-    String c = st3.rest();
-    if (cnx->bot->channelList->getChannel(c) &&
-        cnx->bot->channelList->getChannel(c)->getUser(nick) &&
-        cnx->bot->channelList->getChannel(c)->getUser(nick)->getAop() &&
-        !(cnx->bot->channelList->getChannel(c)->getUser(nick)->mode & User::OP_MODE)
-        && cnx->bot->channelList->getChannel(c)->getUser(nick)->userkey == rest)
-      cnx->queue->sendChannelMode(c, "+o", nick);
-  }
-}
-
-void
-Parser::parsePrivmsg(ServerConnection *cnx,
-                     Person *from, String rest)
-{
-  String nick = from->getNick();
-
-  StringTokenizer st(rest);
-  String to = st.nextToken();
-  String fromUserhost = Utils::getUserhost(from->getAddress());
-
-  rest = st.rest().subString(1);
-
-  if (++(cnx->bot->ignoredUserhosts[fromUserhost])
-      > Bot::MAX_MESSAGES) {
-    if (cnx->bot->ignoredUserhosts[fromUserhost]
-        == Bot::MAX_MESSAGES+1) {
-#ifdef USESCRIPTS
-      cnx->bot->botInterp->RunHooks(Hook::FLOOD, nick,
-                       scm_listify (Utils::string2SCM(nick),
-                               SCM_UNDEFINED));
-#endif
-      cnx->bot->ignoredUserhosts[fromUserhost] += Bot::IGNORE_DELAY;
-      cnx->bot->logLine(from->getAddress() +
-                   " is flooding me. We will ignore him/her/it.");
-      if (!Utils::isChannel(to))
-        from->sendNotice(String("\002You are now being ignored for ") +
-                         String((long)Bot::IGNORE_DELAY) + " seconds.\002");
-    }
-    // The following lines reset the counter if you use the
-    // command "!sorry" (if '!' is your command char).
-    // This is not documented, I know. But one probably does
-    // not want that every users can bypass the flood control
-    // Of course, if you want this feature to remain 'secret',
-    // do not use it in public.
-    if (rest.toUpper() == String(cnx->bot->commandChar) + "SORRY") {
-      cnx->bot->ignoredUserhosts[fromUserhost] = 0;
-      from->sendNotice("\002Don't do it again!\002");
-    }
-    return;
-  }
-
-  if (rest[0] == '\001') {
-    rest = rest.subString(1, rest.length() - 2);
-    if (!Utils::isChannel(to))
-      for (std::map<String, Person *, std::less<String> >::iterator it =
-             cnx->bot->spyList.begin(); it != cnx->bot->spyList.end(); ++it)
-        (*it).second->sendNotice(String("CTCP From ") + nick +
-                                ": " + rest);
-    Parser::parseCTCP(cnx, from, to, rest);
-  }
-  else {
-    if ((rest.length() < 5 ||
-        rest.subString(1, 5).toUpper() != "IDENT") &&
-       (rest.length() < 8 ||
-        rest.subString(1, 8).toUpper() != "PASSWORD") &&
-        !Utils::isChannel(to))
-      for (std::map<String, Person *, std::less<String> >::iterator it =
-             cnx->bot->spyList.begin(); it != cnx->bot->spyList.end(); ++it)
-        (*it).second->sendNotice(String("*") + nick + "* " + rest);
-    Parser::parseMessage(cnx, from, to, rest);
-  }
-}
-
-void
-Parser::parsePart(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  String n = from->getNick();
-  StringTokenizer st(rest);
-  String channel = st.nextToken();
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::LEAVE, n + " " + channel,
-                   scm_listify (Utils::string2SCM(n),
-                           Utils::string2SCM(channel),
-                           SCM_UNDEFINED));
-#endif
-
-  if (n.toLower() == cnx->bot->nickName.toLower()) {
-    cnx->bot->logLine(String("Leaved channel ") + channel + ".");
-    cnx->bot->channelList->delChannel(channel);
-  } else {
-    Channel * c = cnx->bot->channelList->getChannel(channel);
-    if (!c) return;
-    c->delNick(n);
-    if (c->countOp == 0 && c->count == 1) {
-      cnx->queue->sendPart(channel);
-      cnx->queue->sendJoin(channel, cnx->bot->wantedChannels[channel]->key);
-    }
-  }
-}
-
-void
-Parser::parsePing(ServerConnection * cnx,
-                  Person *from, String rest)
-{
-  cnx->queue->sendPong(rest);
-}
-
-void
-Parser::parsePong(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  cnx->lag = (cnx->lag + 2 * (time(NULL) - cnx->pingTime)) / 3;
-  cnx->bot->sentPing = false;
-}
-
-void
-Parser::parseQuit(ServerConnection *cnx,
-                  Person *from, String rest)
-{
-  String n = from->getNick();
-  
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::SIGNOFF, n + " " + rest,
-                   scm_listify (Utils::string2SCM(n),
-                           Utils::string2SCM(rest),
-                           SCM_UNDEFINED));
-#endif
-
-  if (n == cnx->bot->nickName)
-    cnx->bot->stop = true;
-
-  for (std::map<String, Channel *, std::less<String> >::iterator it =
-         cnx->bot->channelList->begin();
-       it != cnx->bot->channelList->end();
-       ++it)
-    (*it).second->delNick(n);
-}
-
-void
-Parser::parseTopic(ServerConnection *cnx,
-                   Person *from, String rest)
-{
-  StringTokenizer st(rest);
-  String channel = st.nextToken();
-  String newTopic = st.rest().subString(1);
-  Channel *c = cnx->bot->channelList->getChannel(channel);
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::TOPIC, from->getNick() + " " + channel +
-                   " " + newTopic,
-                   scm_listify (Utils::string2SCM(from->getNick()),
-                           Utils::string2SCM(channel),
-                           Utils::string2SCM(newTopic),
-                           SCM_UNDEFINED));
-#endif
-
-  if (!c) return;
-
-  if (c->lockedTopic && from->getNick() != cnx->bot->nickName)
-    cnx->queue->sendTopic(channel, c->channelTopic);
-
-  c->channelTopic = newTopic;
-}
-
-void
-Parser::parseCTCP(ServerConnection *cnx,
-                  Person *from, String to,
-                  String parameters)
-{
-  StringTokenizer st(parameters);
-  String command = st.nextToken().toUpper();
-  String nick = from->getNick();
-  String rest;
-
-  if (st.hasMoreTokens())
-    rest = st.rest();
-  else
-    rest = "";
-
-#ifdef USESCRIPTS
-  cnx->bot->botInterp->RunHooks(Hook::CTCP, nick + " " + to + " " +
-                   command + " " + rest,
-                   scm_listify (Utils::string2SCM(nick),
-                           Utils::string2SCM(to),
-                           Utils::string2SCM(command),
-                           Utils::string2SCM(rest),
-                           SCM_UNDEFINED));
-#endif
-
-  if (command == "PING")
-    cnx->queue->sendCTCPReply(nick, "PING", rest);
-  else if (command == "VERSION")
-    cnx->queue->sendCTCPReply(nick, "VERSION", cnx->bot->versionString);
-
-  else if (command == "CLOCK") {
-    time_t diff = time(NULL) - cnx->bot->startTime;
-    cnx->queue->sendCTCPReply(nick, "CLOCK", String("elapsed time: ") +
-                         String((long)(diff / 86400)) + "d" +
-                         String((long)(diff % 86400) / 3600) +
-                         "h" + String((long)(diff % 3600) / 60) +
-                         "m" + String((long)(diff % 60)) + "s");
-  } else if (command == "COMMAND")
-    cnx->queue->sendCTCPReply(nick,
-                         "COMMAND",
-                         String(cnx->bot->commandChar));
-  else if (command == "LAG")
-    cnx->queue->sendCTCPReply(nick, "LAG",
-                         String((long)cnx->lag) + " second(s)");
-  else if (command == "DCC") {
-    StringTokenizer st2(rest);
-    command = st2.nextToken().toUpper();
-    if (command == "CHAT") {
-      // FIXME: Re-activate and debug DCC
-      st2.nextToken();
-      unsigned long address =
-       ntohl (strtoul((const char *)st2.nextToken(), 0, 0));
-      int port = atoi((const char *)st2.nextToken ());
-      if (port >= 1024 && Utils::getLevel(cnx->bot, from->getAddress()))
-       cnx->bot->addDCC(from, address, port);
-    }
-  }
-#ifdef USESCRIPTS
-  else if (command == "ACTION") {
-    cnx->bot->botInterp->RunHooks(Hook::ACTION, from->getAddress() + " " +
-                     to + " " + rest,
-                     scm_listify (Utils::string2SCM(from->getAddress()),
-                             Utils::string2SCM(to),
-                             Utils::string2SCM(rest),
-                             SCM_UNDEFINED));
-  }
-#endif
-}
-
-struct userFunctionsStruct userFunctionsInit[] =
-
-{
-  { "ACTION",      UserCommands::Action,      User::USER,         true  },
-  { "ADDUSER",     UserCommands::AddUser,     User::FRIEND,       false },
-  { "ADDSERVER",   UserCommands::AddServer,   User::FRIEND,       false },
-  { "ADDSHIT",     UserCommands::AddShit,     User::FRIEND,       false },
-  { "ALIAS",       UserCommands::Alias,       User::MASTER,       false },
-  { "BAN",         UserCommands::Ban,         User::USER,         true  },
-  { "BANLIST",     UserCommands::BanList,     User::USER,         true  },
-  //  { "CHANGELEVEL", UserCommands::ChangeLevel, User::FRIEND,       false },
-  { "CHANNELS",    UserCommands::Channels,    User::FRIEND,       false },
-  { "CYCLE",       UserCommands::Cycle,       User::FRIEND,       true  },
-  { "DCCLIST",     UserCommands::DCCList,     User::FRIEND,       false },
-  { "DEBAN",       UserCommands::Deban,       User::USER,         true  },
-  { "DELSERVER",   UserCommands::DelServer,   User::FRIEND,       false },
-  { "DELUSER",     UserCommands::DelUser,     User::FRIEND,       false },
-  { "DELSHIT",     UserCommands::DelShit,     User::FRIEND,       false },
-  { "DEOP",        UserCommands::Deop,        User::TRUSTED_USER, true  },
-  { "DIE",         UserCommands::Die,         User::MASTER,       false },
-  { "DO",          UserCommands::Do,          User::MASTER,       false },
-#ifdef USESCRIPTS
-  { "EXECUTE",     UserCommands::Execute,     User::MASTER,       false },
-#endif
-  { "HELP",        UserCommands::Help,        User::NONE,         false },
-  { "IDENT",       UserCommands::Ident,       User::NONE,         true  },
-  { "INVITE",      UserCommands::Invite,      User::USER,         true  },
-  { "JOIN",        UserCommands::Join,        User::FRIEND,       false },
-  { "KEEP",        UserCommands::Keep,        User::FRIEND,       true  },
-  { "KICK",        UserCommands::Kick,        User::USER,         true  },
-  { "KICKBAN",     UserCommands::KickBan,     User::USER,         true  },
-  { "LOAD",        UserCommands::Load,        User::FRIEND,       false },
-#ifdef USESCRIPTS
-  { "LOADSCRIPT",  UserCommands::LoadScript,  User::MASTER,       false },
-#endif
-  { "LOCK",        UserCommands::Lock,        User::FRIEND,       true  },
-  { "MODE",        UserCommands::Mode,        User::FRIEND,       true  },
-  { "MSG",         UserCommands::Msg,         User::USER,         false },
-  { "NAMES",       UserCommands::Names,       User::USER,         true  },
-  { "NEXTSERVER",  UserCommands::NextServer,  User::FRIEND,       false },
-  { "NICK",        UserCommands::Nick,        User::FRIEND,       false },
-  { "NSLOOKUP",    UserCommands::NsLookup,    User::USER,         false },
-  { "OP",          UserCommands::Op,          User::TRUSTED_USER, true  },
-  { "PART",        UserCommands::Part,        User::FRIEND,       true  },
-  { "PASSWORD",    UserCommands::Password,    User::USER,         true  },
-  { "RECONNECT",   UserCommands::Reconnect,   User::FRIEND,       false },
-  { "RSPYMESSAGE", UserCommands::RSpyMessage, User::USER,         false },
-  { "SAVE",        UserCommands::Save,        User::FRIEND,       false },
-  { "SAY",         UserCommands::Say,         User::USER,         true  },
-  { "SERVER",      UserCommands::Server,      User::FRIEND,       false },
-  { "SERVERLIST",  UserCommands::ServerList,  User::FRIEND,       false },
-  { "SETVERSION",  UserCommands::SetVersion,  User::MASTER,       false },
-  { "SHITLIST",    UserCommands::ShitList,    User::FRIEND,       false },
-  { "SPYLIST",     UserCommands::SpyList,     User::USER,         false },
-  { "SPYMESSAGE",  UserCommands::SpyMessage,  User::USER,         false },
-  { "STATS",       UserCommands::Stats,       User::FRIEND,       true  },
-  { "TBAN",        UserCommands::TBan,        User::USER,         true  },
-  { "TKBAN",       UserCommands::TKBan,       User::USER,         true  },
-  { "TOPIC",       UserCommands::Topic,       User::USER,         true  },
-  { "UNLOCK",      UserCommands::Unlock,      User::FRIEND,       true  },
-  { "USERLIST",    UserCommands::UserList,    User::FRIEND,       false },
-  { "WHO",         UserCommands::Who,         User::NONE,         true  },
-  { "WHOIS",       UserCommands::Whois,       User::FRIEND,       true  },
-  { "",            0,                         0,                  false }
-};
-
-void
-Parser::parseMessage(ServerConnection *cnx,
-                     Person *from, String to,
-                     String parameters)
-{
-#ifdef USESCRIPTS
-  if (Utils::isChannel(to))
-    cnx->bot->botInterp->RunHooks(Hook::PUBLIC, from->getNick() + " " +
-                     to + " " + parameters,
-                     scm_listify (Utils::string2SCM(from->getNick()),
-                             Utils::string2SCM(to),
-                             Utils::string2SCM(parameters),
-                             SCM_UNDEFINED));
-  else
-    cnx->bot->botInterp->RunHooks(Hook::MESSAGE, from->getNick() + " " + parameters,
-                     scm_listify (Utils::string2SCM(from->getNick()),
-                             Utils::string2SCM(parameters),
-                             SCM_UNDEFINED));
-#endif
-
-  if (parameters[0] != cnx->bot->commandChar)
-    return;
-
-  StringTokenizer st(parameters);
-
-  String command = st.nextToken().subString(1).toUpper();
-  String rest = st.rest().trim();
-  int level;
-  bool identified = false;
-
-  std::list<userFunction *>::iterator it;
-  for (it = cnx->bot->userFunctions.begin();
-       it != cnx->bot->userFunctions.end();
-       ++it)
-    if (command == (*it)->name) {
-      if ((*it)->needsChannelName) {
-        if (Utils::isChannel(rest)) {
-          StringTokenizer st2(rest);
-          to = st.nextToken();
-          rest = st.rest();
-        }
-        if (!Utils::isChannel(to)) {
-          from->sendNotice("\002You need to supply a channel name"
-                          " for this command\002");
-          return;
-        }
-        if (!cnx->bot->channelList->getChannel(to)) {
-          from->sendNotice(String("\002I am not on channel\002 ") +
-                          to);
-          return;
-        }
-        level = Utils::getLevel(cnx->bot, from->getAddress(), to);
-        User * u = 0;
-        if (Channel *c = cnx->bot->channelList->getChannel(to))
-          u = c->getUser(from->getNick());
-        if (!u || !u->userListItem)
-          identified = true;
-        else
-          identified = u->userListItem->passwd == "" || u->userListItem->identified > 0;
-      } else {
-        level = Utils::getLevel(cnx->bot, from->getAddress());
-        identified = true;
-      }
-      if (level >= (*it)->minLevel) {
-        cnx->bot->logLine(from->getAddress() + " did " + command +
-                     " " + rest);
-#ifdef USESCRIPTS
-        if ((*it)->argsCount != -1) {
-          Parser::parseScriptFunction(cnx, to, (*it)->needsChannelName,
-                                      (*it)->scmFunc, (*it)->argsCount, rest);
-        } else {
-          (*it)->function(cnx, from, to, rest);
-        }
-#else
-        (*it)->function(cnx, from, to, rest);
-#endif
-        break;
-      } else {
-        if (!identified)
-          from->sendNotice(String("\002You are not identified on channel\002 ")+to);
-      }
-    }
-}
-
-#ifdef USESCRIPTS
-void
-Parser::parseScriptFunction(ServerConnection *cnx, String channel,
-                            bool needsChannelName, SCM scmFunc,
-                            int argsCount, String parameters)
-{
-  String param;
-  SCM args_list = scm_listify (SCM_UNDEFINED);
-
-  if (needsChannelName) {
-    args_list = gh_append2(args_list,
-                           scm_listify (Utils::string2SCM(channel),
-                                   SCM_UNDEFINED));
-    argsCount--;
-  }
-
-  StringTokenizer st(parameters);
-  for (int i = argsCount; i > 0; i--) {
-    if (i == 1)
-      param = st.rest();
-    else
-      param = st.nextToken();
-    args_list = gh_append2(args_list,
-                           scm_listify (Utils::string2SCM(param),
-                                   SCM_UNDEFINED));
-  }
-
-  struct wrapper_data wd;
-  wd.func = scmFunc;
-  wd.args = args_list;
-
-  gh_catch(SCM_BOOL_T, (scm_t_catch_body) scm_apply_wrapper,
-           (void *)&wd, (scm_t_catch_handler) Interp::ErrorHandler,
-           0);
-}
-#endif
+// Parser.C  -*- C++ -*-
+// Copyright (c) 1997, 1998 Etienne BERNARD
+// Copyright (C) 2002 Clinton Ebadi
+
+// 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
+// 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.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "StringTokenizer.H"
+#include "Parser.H"
+#include "UserCommands.H"
+#include "Macros.H"
+#include "Utils.H"
+#include "ShitList.H"
+
+typedef void (*fptr) (ServerConnection *, Person *, String);
+std::map < std::string, fptr, std::less < std::string > >Parser::functions;
+
+void
+Parser::init ()
+{
+  // Parser functions
+  Parser::functions["001"] = Parser::parse001; /* RPL_WELCOME */
+  Parser::functions["302"] = Parser::parse302; /* RPL_USERHOST */
+  Parser::functions["311"] = Parser::parse311; /* RPL_WHOISUSER */
+  Parser::functions["315"] = Parser::parse315; /* RPL_ENDOFWHO */
+  Parser::functions["324"] = Parser::parse324; /* RPL_CHANNELMODEIS */
+  Parser::functions["332"] = Parser::parse332; /* RPL_TOPIC */
+  Parser::functions["352"] = Parser::parse352; /* RPL_WHOREPLY */
+  Parser::functions["353"] = Parser::parse353; /* RPL_NAMESREPLY */
+  Parser::functions["366"] = Parser::parse366; /* RPL_ENDOFNAMES */
+  Parser::functions["367"] = Parser::parse367; /* RPL_BANLIST */
+  Parser::functions["401"] = Parser::parse401; /* ERR_NOSUCHNICK */
+  Parser::functions["433"] = Parser::parse433; /* ERR_NICKNAMEINUSE */
+  Parser::functions["437"] = Parser::parse433; /* ERR_UNAVAILRESOURCE */
+  Parser::functions["471"] = Parser::parse473; /* ERR_CHANNELISFULL */
+  Parser::functions["473"] = Parser::parse473; /* ERR_INVITEONLYCHAN */
+  Parser::functions["474"] = Parser::parse473; /* ERR_BANNEDFROMCHAN */
+  Parser::functions["475"] = Parser::parse473; /* ERR_BADCHANNELKEY */
+  Parser::functions["ERROR"] = Parser::parseError;
+  Parser::functions["INVITE"] = Parser::parseInvite;
+  Parser::functions["JOIN"] = Parser::parseJoin;
+  Parser::functions["KICK"] = Parser::parseKick;
+  Parser::functions["MODE"] = Parser::parseMode;
+  Parser::functions["NICK"] = Parser::parseNick;
+  Parser::functions["NOTICE"] = Parser::parseNotice;
+  Parser::functions["PART"] = Parser::parsePart;
+  Parser::functions["PING"] = Parser::parsePing;
+  Parser::functions["PONG"] = Parser::parsePong;
+  Parser::functions["PRIVMSG"] = Parser::parsePrivmsg;
+  Parser::functions["QUIT"] = Parser::parseQuit;
+  Parser::functions["TOPIC"] = Parser::parseTopic;
+  Parser::functions[""] = Parser::parseError;
+}
+
+
+void
+Parser::parseLine (ServerConnection * cnx, String line)
+{
+  StringTokenizer st (line);
+  Person *from = 0;
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::RAW, line,
+                                scm_listify (Utils::
+                                             string2SCM (line),
+                                             SCM_UNDEFINED));
+#endif
+  if (line[0] == ':')
+    {
+      String fromMask = st.nextToken ().subString (1);
+      if (fromMask.find ('!') != -1)
+       from = new Person (cnx->bot, fromMask);
+    }
+
+  String command = st.nextToken ();
+  String rest = st.rest ();
+  /*  for (int i = 0; functions[i].name != 0; i++)
+     if (command == functions[i].name) {
+     functions[i].function(cnx, from, rest);
+     break;
+     }
+   */
+  if (fptr temp_func = functions[command])
+    temp_func (cnx, from, rest);
+  delete from;
+}
+
+void
+Parser::parse001 (ServerConnection * cnx, Person * from, String rest)
+{
+  String temp = "";
+  StringTokenizer st (rest);
+  String realNick = st.nextToken ();
+  if ((cnx->bot->nickName).toLower () != realNick)
+    {
+      // Yes, this can happen, and it was a very subtle bug
+      cnx->bot->nickName = realNick;
+      cnx->bot->userList->removeFirst ();
+      cnx->bot->userList->addUserFirst (realNick + "!" +
+                                       cnx->bot->userHost, "*", 0,
+                                       3, true, -1, "");
+      cnx->bot->lastNickNameChange = time (0);
+      cnx->bot->rehash ();
+    }
+
+  cnx->bot->connected = true;
+  cnx->queue->sendUserMode (cnx->bot->nickName, "+i");
+  cnx->queue->sendWhois (cnx->bot->nickName);
+  for (std::map < String, wantedChannel *,
+       std::less < String > >::iterator it =
+       cnx->bot->wantedChannels.begin ();
+       it != cnx->bot->wantedChannels.end (); ++it)
+    cnx->queue->sendJoin ((*it).first, (*it).second->key);
+  cnx->bot->logLine (String ("Connected to server ") +
+                    cnx->bot->serverList->currentServer ()->
+                    getHostName () + " (" +
+                    String ((long) cnx->bot->serverList->
+                            currentServer ()->getPort ()) + ").");
+}
+
+void
+Parser::parse302 (ServerConnection * cnx, Person * from, String rest)
+{
+  unsigned long num = cnx->bot->receivedUserhostID++;
+  StringTokenizer st (rest);
+  st.nextToken (':');
+  if (st.rest ().length ())
+    {
+      st.nextToken ('=');
+      String parameters = st.rest ();
+      parameters = parameters.subString (1);
+      cnx->bot->userhostMap[num] = parameters;
+    }
+  else
+    cnx->bot->userhostMap[num] = "";
+}
+
+void
+Parser::parse311 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String nuh = st.nextToken () + "!";
+  String uh = st.nextToken () + "@";
+  uh = uh + st.nextToken ();
+  nuh = nuh + uh;
+  cnx->bot->userList->addUserFirst (nuh, "*", 0, 3, true, -1, "");
+  cnx->bot->userHost = uh;
+}
+
+void
+Parser::parse315 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String channel = st.nextToken ();
+  Channel *c = cnx->bot->channelList->getChannel (channel);
+  if (!c)
+    return;
+  c->gotWho = true;
+}
+
+void
+Parser::parse324 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String channel = st.nextToken ();
+  if (Channel * c = cnx->bot->channelList->getChannel (channel))
+    if (c)
+      c->parseMode (from, st.rest ());
+}
+
+void
+Parser::parse332 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String channel = st.nextToken ();
+  if (Channel * c = cnx->bot->channelList->getChannel (channel))
+    if (c)
+      c->channelTopic = st.rest ().subString (1);
+}
+
+void
+Parser::parse352 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String ch = st.nextToken ();
+  String uh = st.nextToken () + "@";
+  uh = uh + st.nextToken ();
+  st.nextToken ();
+  String n = st.nextToken ();
+  String m = st.nextToken ();
+  int mode = 0;
+  for (int i = 0; i < m.length (); i++)
+    switch (m[i])
+      {
+      case 'H':
+       break;
+      case 'G':
+       mode |= User::AWAY_MODE;
+       break;
+      case '*':
+       mode |= User::IRCOP_MODE;
+       break;
+      case '@':
+       mode |= User::OP_MODE;
+       break;
+      case '+':
+       mode |= User::VOICE_MODE;
+       break;
+      }
+  if (Channel * c = cnx->bot->channelList->getChannel (ch))
+    if (c)
+      c->addNick (n, uh, mode, cnx->bot->userList);
+}
+
+void
+Parser::parse353 (ServerConnection * cnx, Person * from, String rest)
+{
+  int mode = 0;
+  String nick;
+  StringTokenizer st (rest);
+  st.nextToken ();
+  st.nextToken ();
+  Channel *c = cnx->bot->channelList->getChannel (st.nextToken ());
+  if (!c)
+    return;
+  StringTokenizer st2 (st.nextToken (':'));
+  while (st2.hasMoreTokens ())
+    {
+      nick = st2.nextToken ();
+      if (nick[0] == '@')
+       {
+         mode = User::OP_MODE;
+         nick = nick.subString (1);
+       }
+      else if (nick[0] == '+')
+       {
+         mode = User::VOICE_MODE;
+         nick = nick.subString (1);
+       }
+      c->addNick (nick, "", mode, 0, true);
+    }
+}
+
+void
+Parser::parse366 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String ch = st.nextToken ();
+  if (Channel * c = cnx->bot->channelList->getChannel (ch))
+    c->joined = true;
+}
+
+void
+Parser::parse367 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String ch = st.nextToken ();
+  if (Channel * c = cnx->bot->channelList->getChannel (ch))
+    c->addBan (st.nextToken (), -1);
+}
+
+void
+Parser::parse401 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  String nick = st.nextToken ();
+  if (cnx->bot->spyList.find (nick) != cnx->bot->spyList.end ())
+    {
+      delete cnx->bot->spyList[nick];
+      cnx->bot->spyList.erase (nick);
+    }
+}
+
+void
+Parser::parse433 (ServerConnection * cnx, Person * from, String rest)
+{
+  if (cnx->bot->connected)
+    return;
+  if (cnx->bot->nickName.length () == 9)
+    {
+      int i;
+      for (i = 0;
+          i < cnx->bot->nickName.length ()
+          && cnx->bot->nickName[i] == '_'; i++);
+      if (i < cnx->bot->nickName.length ())
+       cnx->bot->nickName =
+         cnx->bot->nickName.subString (0,
+                                       i - 1) + "_" +
+         cnx->bot->nickName.subString (i + 1);
+      else
+       cnx->bot->nickName = cnx->bot->nickName.subString (0, 4) +
+         String ((long) (rand () % 10000));
+    }
+  else
+    cnx->bot->nickName = cnx->bot->nickName + "_";
+  cnx->queue->sendNick (cnx->bot->nickName);
+}
+
+void
+Parser::parse473 (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  st.nextToken ();
+  cnx->bot->logLine (String ("Unable to join channel ") +
+                    st.nextToken () + ".");
+}
+
+void
+Parser::parseError (ServerConnection * cnx, Person * from, String rest)
+{
+  cnx->bot->logLine (String ("Error from server ") +
+                    cnx->bot->serverList->currentServer ()->
+                    getHostName () + " (" +
+                    String ((long) cnx->bot->serverList->
+                            currentServer ()->getPort ()) + ").");
+  cnx->bot->nextServer ();
+}
+
+void
+Parser::parseInvite (ServerConnection * cnx, Person * from, String rest)
+{
+  String nick = from->getNick ();
+  StringTokenizer st (rest);
+  st.nextToken (':');
+  String channel = st.rest ();
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::INVITE,
+                                nick + " " + channel,
+                                scm_listify (Utils::
+                                             string2SCM (nick),
+                                             Utils::
+                                             string2SCM
+                                             (channel), SCM_UNDEFINED));
+#endif
+  if (cnx->bot->wantedChannels.find (channel) !=
+      cnx->bot->wantedChannels.end ())
+    cnx->queue->sendJoin (channel, cnx->bot->wantedChannels[channel]->key);
+}
+
+void
+Parser::parseJoin (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (from->getAddress ());
+  String n = st.nextToken ('!');
+  String uh = st.nextToken ();
+  StringTokenizer st2 (rest);
+  String c = st2.nextToken (':');
+  String mode;
+  bool joinAndMode = false;
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::JOIN, n + " " + c,
+                                scm_listify (Utils::
+                                             string2SCM (n),
+                                             Utils::
+                                             string2SCM (c), SCM_UNDEFINED));
+#endif
+  // This part of code is for the combined JOIN & MODE of ircd 2.9
+  if (c.find ('\007') >= 0)
+    {
+      joinAndMode = true;
+      StringTokenizer st3 (c);
+      c = st3.nextToken ('\007');
+      String m = st3.rest ();
+      mode = c + " +" + m;
+      for (int i = 0; i < m.length (); i++)
+       mode = mode + " " + n;
+    }
+
+  if (n == cnx->bot->nickName)
+    {
+      cnx->bot->logLine (String ("Joined channel ") + c + ".");
+      if (cnx->bot->wantedChannels.find (c) !=
+         cnx->bot->wantedChannels.end ())
+       cnx->bot->channelList->
+         addChannel (cnx, c, cnx->bot->wantedChannels[c]->keep);
+      else
+       cnx->bot->channelList->addChannel (cnx, c);
+      cnx->queue->sendWho (c);
+      cnx->queue->sendChannelMode (String ("MODE ") + c + " b");
+      cnx->queue->sendChannelMode (String ("MODE ") + c);
+    }
+  else
+    {
+      Channel *ch = cnx->bot->channelList->getChannel (c);
+      if (!ch)
+       return;
+      ShitEntry *se = cnx->bot->shitList->getShit (n + "!" + uh, c);
+      if (se && se->isStillValid () &&
+         se->getShitLevel () >= ShitEntry::SHIT_NOJOIN)
+       {
+         cnx->queue->sendChannelMode (c, "+b", se->getMask ());
+         cnx->queue->sendKick (c, n, se->getShitReason ());
+         return;
+       }
+      ch->addNick (n, uh, 0, cnx->bot->userList);
+      if (ch->getUser (n)->getAop ()
+         && !(ch->getUser (n)->mode & User::OP_MODE) && cnx->bot->iAmOp (c))
+       {
+         // This is a part of the antispoof code
+         ch->getUser (n)->userkey = Utils::getKey ();
+         cnx->queue->sendCTCP (n, "PING",
+                               ch->getUser (n)->userkey + " " + c);
+       }
+    }
+
+  if (joinAndMode)
+    parseMode (cnx, 0, mode);
+}
+
+void
+Parser::parseKick (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  String channel = st.nextToken ();
+  String target = st.nextToken ();
+  String reason = st.rest ().subString (1);
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::KICK,
+                                target + " " +
+                                from->getNick () + " " +
+                                channel + " " + reason,
+                                scm_listify (Utils::
+                                             string2SCM
+                                             (target),
+                                             Utils::
+                                             string2SCM (from->
+                                                         getNick
+                                                         ()),
+                                             Utils::
+                                             string2SCM
+                                             (channel),
+                                             Utils::
+                                             string2SCM
+                                             (reason), SCM_UNDEFINED));
+#endif
+  if (target == cnx->bot->nickName)
+    {
+      cnx->bot->logLine (from->getAddress () +
+                        " kicked me out of channel " + channel +
+                        " (" + reason + ").");
+      cnx->queue->sendJoin (channel,
+                           cnx->bot->channelList->
+                           getChannel (channel)->channelKey);
+      cnx->bot->channelList->delChannel (channel);
+    }
+  else
+    {
+      if (!cnx->bot->channelList->getChannel (channel))
+       return;
+      User *u = cnx->bot->channelList->getChannel (channel)->getUser (target);
+      if (u && u->getProt () >= User::NO_KICK)
+       {
+         String fromNick = from->getNick ();
+         User *v =
+           cnx->bot->channelList->getChannel (channel)->getUser (fromNick);
+         if (v->getProt () < User::NO_KICK)
+           {
+             cnx->bot->logLine (from->getAddress () + " kicked " + target +
+                                " (protected) out of channel " + channel +
+                                " (" + reason + ").");
+             cnx->queue->sendKick (channel, fromNick,
+                                   target + " \002is protected !\002");
+           }
+       }
+      cnx->bot->channelList->getChannel (channel)->delNick (target);
+    }
+}
+
+void
+Parser::parseMode (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  String ch = st.nextToken ();
+  String modes = st.rest ();
+#ifdef USESCRIPTS
+  if (from)
+    cnx->bot->botInterp->RunHooks (Hook::MODE,
+                                  from->getNick () + " " + ch +
+                                  " " + modes,
+                                  scm_listify (Utils::
+                                               string2SCM (from->
+                                                           getNick
+                                                           ()),
+                                               Utils::
+                                               string2SCM (ch),
+                                               Utils::
+                                               string2SCM (modes),
+                                               SCM_UNDEFINED));
+#endif
+  if (Utils::isChannel (ch))
+    {
+      Channel *c = cnx->bot->channelList->getChannel (ch);
+      if (!c)
+       return;
+      if (from)
+       c->parseMode (from, modes);
+      else
+       c->parseMode (0, modes);
+    }
+}
+
+void
+Parser::parseNick (ServerConnection * cnx, Person * from, String rest)
+{
+  String on_orig = from->getNick ();
+  String on = on_orig.toLower ();
+  String nn = rest.subString (1);
+  String nn_lower = nn.toLower ();
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::NICKNAME,
+                                on_orig + " " + nn,
+                                scm_listify (Utils::
+                                             string2SCM
+                                             (on_orig),
+                                             Utils::
+                                             string2SCM (nn),
+                                             SCM_UNDEFINED));
+#endif
+  if ((cnx->bot->nickName).toLower () == on)
+    {
+      cnx->bot->userList->removeFirst ();
+      cnx->bot->userList->addUserFirst (nn + "!" +
+                                       cnx->bot->userHost, "*", 0,
+                                       3, true, -1, "");
+      cnx->bot->lastNickNameChange = time (0);
+      cnx->bot->nickName = nn;
+      cnx->bot->rehash ();
+    }
+
+  if (cnx->bot->spyList.find (on) != cnx->bot->spyList.end ())
+    {
+      cnx->bot->spyList[nn_lower] = cnx->bot->spyList[on];
+      cnx->bot->spyList.erase (on);
+    }
+
+  for (std::map < String, Channel *,
+       std::less < String > >::iterator it =
+       cnx->bot->channelList->begin ();
+       it != cnx->bot->channelList->end (); ++it)
+    if ((*it).second->hasNick (on))
+      (*it).second->changeNick (on, nn_lower);
+}
+
+void
+Parser::parseNotice (ServerConnection * cnx, Person * from, String rest)
+{
+  String nick = "";
+  if (from)
+    nick = from->getNick ();
+  StringTokenizer st (rest);
+  String to = st.nextToken ();
+  rest = st.rest ().subString (1);
+  if (rest[0] != '\001')
+    {
+#ifdef USESCRIPTS
+      if (Utils::isChannel (to))
+       cnx->bot->botInterp->RunHooks (Hook::PUBLIC_NOTICE,
+                                      nick + " " + to + " " + rest,
+                                      scm_listify (Utils::
+                                                   string2SCM (nick),
+                                                   Utils::
+                                                   string2SCM (to),
+                                                   Utils::
+                                                   string2SCM (rest),
+                                                   SCM_UNDEFINED));
+      else
+       cnx->bot->botInterp->RunHooks (Hook::NOTICE, nick + " " + rest,
+                                      scm_listify (Utils::
+                                                   string2SCM (nick),
+                                                   Utils::
+                                                   string2SCM (rest),
+                                                   SCM_UNDEFINED));
+#endif
+      return;
+    }
+
+  rest = rest.subString (1, rest.length () - 2);
+  StringTokenizer st2 (rest);
+  String command = st2.nextToken ();
+  rest = st2.rest ();
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::CTCP_REPLY,
+                                nick + " " + command + " " +
+                                rest,
+                                scm_listify (Utils::
+                                             string2SCM (nick),
+                                             Utils::
+                                             string2SCM
+                                             (command),
+                                             Utils::
+                                             string2SCM (rest),
+                                             SCM_UNDEFINED));
+#endif
+  if (command == "PING")
+    {
+      StringTokenizer st3 (rest);
+      rest = st3.nextToken ();
+      String c = st3.rest ();
+      if (cnx->bot->channelList->getChannel (c) &&
+         cnx->bot->channelList->getChannel (c)->getUser (nick) &&
+         cnx->bot->channelList->getChannel (c)->getUser (nick)->
+         getAop ()
+         && !(cnx->bot->channelList->getChannel (c)->
+              getUser (nick)->mode & User::OP_MODE)
+         && cnx->bot->channelList->getChannel (c)->getUser (nick)->
+         userkey == rest)
+       cnx->queue->sendChannelMode (c, "+o", nick);
+    }
+}
+
+void
+Parser::parsePrivmsg (ServerConnection * cnx, Person * from, String rest)
+{
+  String nick = from->getNick ();
+  StringTokenizer st (rest);
+  String to = st.nextToken ();
+  String fromUserhost = Utils::getUserhost (from->getAddress ());
+  rest = st.rest ().subString (1);
+  if (++(cnx->bot->ignoredUserhosts[fromUserhost]) > Bot::MAX_MESSAGES)
+    {
+      if (cnx->bot->ignoredUserhosts[fromUserhost] == Bot::MAX_MESSAGES + 1)
+       {
+#ifdef USESCRIPTS
+         cnx->bot->botInterp->RunHooks (Hook::FLOOD, nick,
+                                        scm_listify (Utils::
+                                                     string2SCM (nick),
+                                                     SCM_UNDEFINED));
+#endif
+         cnx->bot->ignoredUserhosts[fromUserhost] += Bot::IGNORE_DELAY;
+         cnx->bot->logLine (from->getAddress () +
+                            " is flooding me. We will ignore him/her/it.");
+         if (!Utils::isChannel (to))
+           from->
+             sendNotice (String ("\002You are now being ignored for ") +
+                         String ((long) Bot::IGNORE_DELAY) +
+                         " seconds.\002");
+       }
+      // The following lines reset the counter if you use the
+      // command "!sorry" (if '!' is your command char).
+      // This is not documented, I know. But one probably does
+      // not want that every users can bypass the flood control
+      // Of course, if you want this feature to remain 'secret',
+      // do not use it in public.
+      if (rest.toUpper () == String (cnx->bot->commandChar) + "SORRY")
+       {
+         cnx->bot->ignoredUserhosts[fromUserhost] = 0;
+         from->sendNotice ("\002Don't do it again!\002");
+       }
+      return;
+    }
+
+  if (rest[0] == '\001')
+    {
+      rest = rest.subString (1, rest.length () - 2);
+      if (!Utils::isChannel (to))
+       for (std::map < String, Person *,
+            std::less < String > >::iterator it =
+            cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it)
+         (*it).second->sendNotice (String ("CTCP From ") +
+                                   nick + ": " + rest);
+      Parser::parseCTCP (cnx, from, to, rest);
+    }
+  else
+    {
+      if ((rest.length () < 5 ||
+          rest.subString (1, 5).toUpper () != "IDENT") &&
+         (rest.length () < 8 ||
+          rest.subString (1, 8).toUpper () != "PASSWORD") &&
+         !Utils::isChannel (to))
+       for (std::map < String, Person *,
+            std::less < String > >::iterator it =
+            cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it)
+         (*it).second->sendNotice (String ("*") + nick + "* " + rest);
+      Parser::parseMessage (cnx, from, to, rest);
+    }
+}
+
+void
+Parser::parsePart (ServerConnection * cnx, Person * from, String rest)
+{
+  String n = from->getNick ();
+  StringTokenizer st (rest);
+  String channel = st.nextToken ();
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::LEAVE, n + " " + channel,
+                                scm_listify (Utils::
+                                             string2SCM (n),
+                                             Utils::
+                                             string2SCM
+                                             (channel), SCM_UNDEFINED));
+#endif
+  if (n.toLower () == cnx->bot->nickName.toLower ())
+    {
+      cnx->bot->logLine (String ("Leaved channel ") + channel + ".");
+      cnx->bot->channelList->delChannel (channel);
+    }
+  else
+    {
+      Channel *c = cnx->bot->channelList->getChannel (channel);
+      if (!c)
+       return;
+      c->delNick (n);
+      if (c->countOp == 0 && c->count == 1)
+       {
+         cnx->queue->sendPart (channel);
+         cnx->queue->sendJoin (channel,
+                               cnx->bot->wantedChannels[channel]->key);
+       }
+    }
+}
+
+void
+Parser::parsePing (ServerConnection * cnx, Person * from, String rest)
+{
+  cnx->queue->sendPong (rest);
+}
+
+void
+Parser::parsePong (ServerConnection * cnx, Person * from, String rest)
+{
+  cnx->lag = (cnx->lag + 2 * (time (NULL) - cnx->pingTime)) / 3;
+  cnx->bot->sentPing = false;
+}
+
+void
+Parser::parseQuit (ServerConnection * cnx, Person * from, String rest)
+{
+  String n = from->getNick ();
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::SIGNOFF, n + " " + rest,
+                                scm_listify (Utils::
+                                             string2SCM (n),
+                                             Utils::
+                                             string2SCM (rest),
+                                             SCM_UNDEFINED));
+#endif
+  if (n == cnx->bot->nickName)
+    cnx->bot->stop = true;
+  for (std::map < String, Channel *,
+       std::less < String > >::iterator it =
+       cnx->bot->channelList->begin ();
+       it != cnx->bot->channelList->end (); ++it)
+    (*it).second->delNick (n);
+}
+
+void
+Parser::parseTopic (ServerConnection * cnx, Person * from, String rest)
+{
+  StringTokenizer st (rest);
+  String channel = st.nextToken ();
+  String newTopic = st.rest ().subString (1);
+  Channel *c = cnx->bot->channelList->getChannel (channel);
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::TOPIC,
+                                from->getNick () + " " +
+                                channel + " " + newTopic,
+                                scm_listify (Utils::
+                                             string2SCM (from->
+                                                         getNick
+                                                         ()),
+                                             Utils::
+                                             string2SCM
+                                             (channel),
+                                             Utils::
+                                             string2SCM
+                                             (newTopic), SCM_UNDEFINED));
+#endif
+  if (!c)
+    return;
+  if (c->lockedTopic && from->getNick () != cnx->bot->nickName)
+    cnx->queue->sendTopic (channel, c->channelTopic);
+  c->channelTopic = newTopic;
+}
+
+void
+Parser::parseCTCP (ServerConnection * cnx,
+                  Person * from, String to, String parameters)
+{
+  StringTokenizer st (parameters);
+  String command = st.nextToken ().toUpper ();
+  String nick = from->getNick ();
+  String rest;
+  if (st.hasMoreTokens ())
+    rest = st.rest ();
+  else
+    rest = "";
+#ifdef USESCRIPTS
+  cnx->bot->botInterp->RunHooks (Hook::CTCP,
+                                nick + " " + to + " " +
+                                command + " " + rest,
+                                scm_listify (Utils::
+                                             string2SCM (nick),
+                                             Utils::
+                                             string2SCM (to),
+                                             Utils::
+                                             string2SCM
+                                             (command),
+                                             Utils::
+                                             string2SCM (rest),
+                                             SCM_UNDEFINED));
+#endif
+  if (command == "PING")
+    cnx->queue->sendCTCPReply (nick, "PING", rest);
+  else if (command == "VERSION")
+    cnx->queue->sendCTCPReply (nick, "VERSION", cnx->bot->versionString);
+  else if (command == "CLOCK")
+    {
+      time_t diff = time (NULL) - cnx->bot->startTime;
+      cnx->queue->sendCTCPReply (nick, "CLOCK",
+                                String ("elapsed time: ") +
+                                String ((long) (diff / 86400)) +
+                                "d" +
+                                String ((long) (diff % 86400) /
+                                        3600) + "h" +
+                                String ((long) (diff % 3600) / 60) +
+                                "m" + String ((long) (diff % 60)) + "s");
+    }
+  else if (command == "COMMAND")
+    cnx->queue->sendCTCPReply (nick,
+                              "COMMAND", String (cnx->bot->commandChar));
+  else if (command == "LAG")
+    cnx->queue->sendCTCPReply (nick, "LAG",
+                              String ((long) cnx->lag) + " second(s)");
+  else if (command == "DCC")
+    {
+      StringTokenizer st2 (rest);
+      command = st2.nextToken ().toUpper ();
+      if (command == "CHAT")
+       {
+         // FIXME: Re-activate and debug DCC
+         st2.nextToken ();
+         unsigned long address =
+           ntohl (strtoul ((const char *) st2.nextToken (), 0, 0));
+         int port = atoi ((const char *) st2.nextToken ());
+         if (port >= 1024 && Utils::getLevel (cnx->bot, from->getAddress ()))
+           cnx->bot->addDCC (from, address, port);
+       }
+    }
+#ifdef USESCRIPTS
+  else if (command == "ACTION")
+    {
+      cnx->bot->botInterp->RunHooks (Hook::ACTION,
+                                    from->getAddress () + " " + to +
+                                    " " + rest,
+                                    scm_listify (Utils::
+                                                 string2SCM (from->
+                                                             getAddress
+                                                             ()),
+                                                 Utils::
+                                                 string2SCM (to),
+                                                 Utils::
+                                                 string2SCM (rest),
+                                                 SCM_UNDEFINED));
+    }
+#endif
+}
+
+// struct userFunctionsStruct userFunctionsInit[] = {
+//   {
+//    "ACTION", UserCommands::Action, User::USER, true}
+//   ,
+//   {
+//    "ADDUSER", UserCommands::AddUser, User::FRIEND, false}
+//   ,
+//   {
+//    "ADDSERVER", UserCommands::AddServer, User::FRIEND, false}
+//   ,
+//   {
+//    "ADDSHIT", UserCommands::AddShit, User::FRIEND, false}
+//   ,
+//   {
+//    "ALIAS", UserCommands::Alias, User::MASTER, false}
+//   ,
+//   {
+//    "BAN", UserCommands::Ban, User::USER, true}
+//   ,
+//   {
+//    "BANLIST", UserCommands::BanList, User::USER, true}
+//   ,
+//   //  { "CHANGELEVEL", UserCommands::ChangeLevel, User::FRIEND,       false },
+//   {
+//    "CHANNELS", UserCommands::Channels, User::FRIEND, false}
+//   ,
+//   {
+//    "CYCLE", UserCommands::Cycle, User::FRIEND, true}
+//   ,
+//   {
+//    "DCCLIST", UserCommands::DCCList, User::FRIEND, false}
+//   ,
+//   {
+//    "DEBAN", UserCommands::Deban, User::USER, true}
+//   ,
+//   {
+//    "DELSERVER", UserCommands::DelServer, User::FRIEND, false}
+//   ,
+//   {
+//    "DELUSER", UserCommands::DelUser, User::FRIEND, false}
+//   ,
+//   {
+//    "DELSHIT", UserCommands::DelShit, User::FRIEND, false}
+//   ,
+//   {
+//    "DEOP", UserCommands::Deop, User::TRUSTED_USER, true}
+//   ,
+//   {
+//    "DIE", UserCommands::Die, User::MASTER, false}
+//   ,
+//   {
+//    "DO", UserCommands::Do, User::MASTER, false}
+//   ,
+// #ifdef USESCRIPTS
+//   {
+//    "EXECUTE", UserCommands::Execute, User::MASTER, false}
+//   ,
+// #endif
+//   {
+//    "HELP", UserCommands::Help, User::NONE, false}
+//   ,
+//   {
+//    "IDENT", UserCommands::Ident, User::NONE, true}
+//   ,
+//   {
+//    "INVITE", UserCommands::Invite, User::USER, true}
+//   ,
+//   {
+//    "JOIN", UserCommands::Join, User::FRIEND, false}
+//   ,
+//   {
+//    "KEEP", UserCommands::Keep, User::FRIEND, true}
+//   ,
+//   {
+//    "KICK", UserCommands::Kick, User::USER, true}
+//   ,
+//   {
+//    "KICKBAN", UserCommands::KickBan, User::USER, true}
+//   ,
+//   {
+//    "LOAD", UserCommands::Load, User::FRIEND, false}
+//   ,
+// #ifdef USESCRIPTS
+//   {
+//    "LOADSCRIPT", UserCommands::LoadScript, User::MASTER, false}
+//   ,
+// #endif
+//   {
+//    "LOCK", UserCommands::Lock, User::FRIEND, true}
+//   ,
+//   {
+//    "MODE", UserCommands::Mode, User::FRIEND, true}
+//   ,
+//   {
+//    "MSG", UserCommands::Msg, User::USER, false}
+//   ,
+//   {
+//    "NAMES", UserCommands::Names, User::USER, true}
+//   ,
+//   {
+//    "NEXTSERVER", UserCommands::NextServer, User::FRIEND, false}
+//   ,
+//   {
+//    "NICK", UserCommands::Nick, User::FRIEND, false}
+//   ,
+//   {
+//    "NSLOOKUP", UserCommands::NsLookup, User::USER, false}
+//   ,
+//   {
+//    "OP", UserCommands::Op, User::TRUSTED_USER, true}
+//   ,
+//   {
+//    "PART", UserCommands::Part, User::FRIEND, true}
+//   ,
+//   {
+//    "PASSWORD", UserCommands::Password, User::USER, true}
+//   ,
+//   {
+//    "RECONNECT", UserCommands::Reconnect, User::FRIEND, false}
+//   ,
+//   {
+//    "RSPYMESSAGE", UserCommands::RSpyMessage, User::USER, false}
+//   ,
+//   {
+//    "SAVE", UserCommands::Save, User::FRIEND, false}
+//   ,
+//   {
+//    "SAY", UserCommands::Say, User::USER, true}
+//   ,
+//   {
+//    "SERVER", UserCommands::Server, User::FRIEND, false}
+//   ,
+//   {
+//    "SERVERLIST", UserCommands::ServerList, User::FRIEND, false}
+//   ,
+//   {
+//    "SETVERSION", UserCommands::SetVersion, User::MASTER, false}
+//   ,
+//   {
+//    "SHITLIST", UserCommands::ShitList, User::FRIEND, false}
+//   ,
+//   {
+//    "SPYLIST", UserCommands::SpyList, User::USER, false}
+//   ,
+//   {
+//    "SPYMESSAGE", UserCommands::SpyMessage, User::USER, false}
+//   ,
+//   {
+//    "STATS", UserCommands::Stats, User::FRIEND, true}
+//   ,
+//   {
+//    "TBAN", UserCommands::TBan, User::USER, true}
+//   ,
+//   {
+//    "TKBAN", UserCommands::TKBan, User::USER, true}
+//   ,
+//   {
+//    "TOPIC", UserCommands::Topic, User::USER, true}
+//   ,
+//   {
+//    "UNLOCK", UserCommands::Unlock, User::FRIEND, true}
+//   ,
+//   {
+//    "USERLIST", UserCommands::UserList, User::FRIEND, false}
+//   ,
+//   {
+//    "WHO", UserCommands::Who, User::NONE, true}
+//   ,
+//   {
+//    "WHOIS", UserCommands::Whois, User::FRIEND, true}
+//   ,
+//   {
+//    "", 0, 0, false}
+// };
+void
+Parser::parseMessage (ServerConnection * cnx,
+                     Person * from, String to, String parameters)
+{
+#ifdef USESCRIPTS
+  if (Utils::isChannel (to))
+    cnx->bot->botInterp->RunHooks (Hook::PUBLIC,
+                                  from->getNick () + " " + to +
+                                  " " + parameters,
+                                  scm_listify (Utils::
+                                               string2SCM (from->
+                                                           getNick
+                                                           ()),
+                                               Utils::
+                                               string2SCM (to),
+                                               Utils::
+                                               string2SCM
+                                               (parameters), SCM_UNDEFINED));
+  else
+    cnx->bot->botInterp->RunHooks (Hook::MESSAGE,
+                                  from->getNick () + " " +
+                                  parameters,
+                                  scm_listify (Utils::
+                                               string2SCM (from->
+                                                           getNick
+                                                           ()),
+                                               Utils::
+                                               string2SCM
+                                               (parameters), SCM_UNDEFINED));
+#endif
+  if (parameters[0] != cnx->bot->commandChar)
+    return;
+
+  StringTokenizer st (parameters);
+  String command = st.nextToken ().subString (1).toUpper ();
+  String rest = st.rest ().trim ();
+  int level;
+  bool identified = false;
+  userFunction * f = cnx->bot->userFunctions[command];
+  if (f)
+    {
+      if (f->needsChannelName)
+       {
+         if (Utils::isChannel (rest))
+           {
+             StringTokenizer st2 (rest);
+             to = st.nextToken ();
+             rest = st.rest ();
+           }
+           if (!Utils::isChannel (to))
+             {
+               from->sendNotice ("\002You need to supply a channel name"
+                                 " for this command\002");
+               return;
+             }
+           if (!cnx->bot->channelList->getChannel (to))
+             {
+               from->sendNotice (String ("\002I am not on channel\002 ") +
+                                 to);
+               return;
+             }
+           level = Utils::getLevel (cnx->bot, from->getAddress (), to);
+           User *u = 0;
+           if (Channel * c = cnx->bot->channelList->getChannel (to))
+             u = c->getUser (from->getNick ());
+           if (!u || !u->userListItem)
+             identified = true;
+           else
+             identified = u->userListItem->passwd == ""
+               || u->userListItem->identified > 0;
+         }
+       else
+         {
+           level = Utils::getLevel (cnx->bot, from->getAddress ());
+           identified = true;
+         }
+       if (level >= f->minLevel)
+         {
+           cnx->bot->logLine (from->getAddress () + " did " + command +
+                              " " + rest);
+#ifdef USESCRIPTS
+           if (f->argsCount != -1)
+             {
+               Parser::parseScriptFunction (cnx, to, f->needsChannelName,
+                                            f->scmFunc, f->argsCount,
+                                            rest);
+             }
+           else
+             {
+               f->function (cnx, from, to, rest);
+             }
+#else
+           f->function (cnx, from, to, rest);
+#endif
+         }
+       else
+         {
+           if (!identified)
+             from->
+               sendNotice (String
+                           ("\002You are not identified on channel\002 ") +
+                           to);
+         }
+      }
+}
+
+#ifdef USESCRIPTS
+void
+Parser::parseScriptFunction (ServerConnection * cnx,
+                            String channel,
+                            bool needsChannelName,
+                            SCM scmFunc, int argsCount, String parameters)
+{
+  String param;
+  SCM args_list = scm_listify (SCM_UNDEFINED);
+  if (needsChannelName)
+    {
+      args_list = gh_append2 (args_list,
+                             scm_listify (Utils::
+                                          string2SCM (channel),
+                                          SCM_UNDEFINED));
+      argsCount--;
+    }
+
+  StringTokenizer st (parameters);
+  for (int i = argsCount; i > 0; i--)
+    {
+      if (i == 1)
+       param = st.rest ();
+      else
+       param = st.nextToken ();
+      args_list = gh_append2 (args_list,
+                             scm_listify (Utils::string2SCM (param),
+                                          SCM_UNDEFINED));
+    }
+
+  struct wrapper_data wd;
+  wd.func = scmFunc;
+  wd.args = args_list;
+  gh_catch (SCM_BOOL_T, (scm_t_catch_body) scm_apply_wrapper,
+           (void *) &wd, (scm_t_catch_handler) Interp::ErrorHandler, 0);
+}
+#endif
index ca36233..9007de8 100644 (file)
 #include <map>
 #include <string>
 
-struct userFunctionsStruct {
-  String name;
-  void (*function)(ServerConnection *, Person *, String, String);
-  int minLevel;
-  bool needsChannelName;
-};
+// struct userFunctionsStruct {
+//   //  String name;
+//   void (*function)(ServerConnection *, Person *, String, String);
+//   int minLevel;
+//   bool needsChannelName;
+// };
 
 class userFunction {
 public:
-  String name;
   void (*function)(ServerConnection *, Person *, String, String);
   int minLevel;
   bool needsChannelName;
@@ -53,27 +52,30 @@ public:
   SCM scmFunc;
 #endif
 
-  userFunction(String na,
-               void (*f)(ServerConnection *, Person *,
-                         String, String),
-               int m, bool n
+  userFunction(void (*f)(ServerConnection *, Person *,
+                         String, String) = 0,
+               int m = 0 , bool n = false
 #ifdef USESCRIPTS
                , int a = -1, SCM scm_f = 0
 #endif
                )
-    : name(na), function(f), minLevel(m), needsChannelName(n)
+    : function(f), minLevel(m), needsChannelName(n)
 #ifdef USESCRIPTS
       ,argsCount(a), scmFunc(scm_f)
 #endif
     {
 #ifdef USESCRIPTS
-//      scm_protect_object(scm_f);
+      if (scmFunc)
+       scm_gc_protect_object(scmFunc);
 #endif
     }
 
 #ifdef USESCRIPTS
-//  ~userFunction()
-//    { scm_unprotect_object(scmFunc); };
+  ~userFunction()
+  { 
+    if (scmFunc)
+      scm_gc_unprotect_object(scmFunc); 
+  }
 #endif
 };
 
@@ -123,6 +125,7 @@ private:
   
   typedef void (*fptr)(ServerConnection *, Person *, String);
   static std::map<std::string, fptr, std::less<std::string> > functions;
+  
 };
 
 #endif
index 8fb7604..2c90b8e 100644 (file)
@@ -25,6 +25,7 @@
 #include "Utils.H"
 #include "Server.H"
 #include "ServerList.H"
+#include "ServerQueue.H"
 #include "ScriptCommands.H"
 #include "Interp.H"
 #include <libguile.h>
@@ -423,9 +424,6 @@ ScriptCommands::Unlock(SCM channel)
   return scm_long2num(m.getCode());
 }
 
-
-
-
 SCM
 ScriptCommands::getNickname(void)
 {
@@ -502,17 +500,13 @@ ScriptCommands::addCommand(SCM scm_commandName, SCM scm_function,
 
   // We check that the command does not exist
   String commandName = Utils::scm2String(scm_commandName).toUpper();
-  std::list<class userFunction *>::iterator it =
-    Interp::bot->userFunctions.begin();
-  for ( ; it != Interp::bot->userFunctions.end(); ++it) {
-    if ((*it)->name == commandName)
-      return SCM_BOOL_F;
-  }
+  if (Interp::bot->userFunctions[commandName])
+    return SCM_BOOL_F;
 
   // Next we check that needsChannel is a boolean
   if (!gh_boolean_p(scm_needsChannel))
     return SCM_BOOL_F;
-  bool needsChannel = (bool)gh_scm2bool(scm_needsChannel);
+  bool needsChannel = gh_scm2bool(scm_needsChannel);
 
   // We check that minLevel is an integer and that it's
   // a valid level
@@ -536,9 +530,9 @@ ScriptCommands::addCommand(SCM scm_commandName, SCM scm_function,
     return SCM_BOOL_F;
 
   // We add the command in the commands list
-  Interp::bot->userFunctions.push_back(new userFunction(commandName, 
-(void (*)(ServerConnection *, Person *, String, String))0, minLevel, needsChannel, args, scm_function));
-
+  Interp::bot->userFunctions[commandName] = 
+    new userFunction(0, minLevel, needsChannel, args, scm_function);
+  
   return SCM_BOOL_T;
 }
 
@@ -551,19 +545,11 @@ ScriptCommands::delCommand(SCM scm_commandName)
 
   // We check that the command does exist
   String commandName = Utils::scm2String(scm_commandName).toUpper();
-  std::list<class userFunction *>::iterator it =
-    Interp::bot->userFunctions.begin();
-  for ( ; it != Interp::bot->userFunctions.end(); ++it) {
-    if ((*it)->name == commandName)
-      break;
-  }
-
-  if (it == Interp::bot->userFunctions.end())
+  if (!Interp::bot->userFunctions[commandName])
     return SCM_BOOL_F;
 
   // We delete the command
-  Interp::bot->userFunctions.erase(it);
-  delete (*it);
+  Interp::bot->userFunctions.erase(commandName);
 
   return SCM_BOOL_T;
 }
@@ -592,7 +578,25 @@ ScriptCommands::AddTimer(SCM when, SCM function)
 SCM
 ScriptCommands::DelTimer(SCM timer)
 {
-  return gh_bool2scm(Interp::bot->botInterp->DelTimer(timer));
+  return SCM_BOOL (Interp::bot->botInterp->DelTimer(timer));
+}
+
+// Message sending
+// FIXME: write these
+
+#define IQUEUE Interp::bot->serverConnection->queue
+
+SCM
+ScriptCommands::sendCTCP(SCM to, SCM command , SCM message)
+{
+  VERIFY_STRING(to);
+  VERIFY_STRING(command);
+  VERIFY_STRING(message);
+
+  IQUEUE->sendCTCP (Utils::scm2String (to), Utils::scm2String (command),
+                  Utils::scm2String (message));
+  return SCM_UNSPECIFIED;
 }
 
+
 #endif
index dc8d243..d9ec71f 100644 (file)
@@ -77,6 +77,32 @@ public:
   static SCM AddHook(SCM, SCM, SCM, SCM, SCM);
   static SCM AddTimer(SCM, SCM);
   static SCM DelTimer(SCM);
+
+  // Message sending
+  static SCM sendCTCP(SCM, SCM, SCM);
+  /*
+  SCM sendCTCPReply(SCM, SCM, SCM);
+  SCM sendChannelMode(SCM);
+  SCM sendChannelMode(SCM, SCM, SCM);
+  SCM sendInvite(SCM, SCM);
+  SCM sendJoin(SCM, SCM);
+  SCM sendKick(SCM, SCM, SCM);
+  SCM sendNick(SCM);
+  SCM sendNotice(SCM, SCM);
+  SCM sendPart(SCM);
+  SCM sendPass(SCM);
+  SCM sendPing(SCM);
+  SCM sendPong(SCM);
+  SCM sendPrivmsg(SCM, SCM);
+  SCM sendQuit(SCM);
+  SCM sendTopic(SCM, SCM);
+  SCM sendUser(SCM, SCM);
+  SCM sendUserMode(SCM, SCM);
+  SCM sendUserhost(SCM);
+  SCM sendWho(SCM);
+  SCM sendWhois(SCM);
+  */
+
 };
 
 #endif
index 0b01aaa..abb5b38 100644 (file)
@@ -21,6 +21,7 @@
 #endif
 
 #include <fstream>
+#include <map>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -249,7 +250,6 @@ UserCommands::Alias(ServerConnection *cnx, Person *from,
   StringTokenizer st(rest);
   String newF = st.nextToken().toUpper();
   String oldF = st.nextToken().toUpper();
-  std::list<userFunction *>::iterator it;
 
   if (newF == "" || oldF == "") {
     from->sendNotice("\002Invalid syntax for this command.\002");
@@ -257,36 +257,22 @@ UserCommands::Alias(ServerConnection *cnx, Person *from,
   }
 
   // First, we check that the "new" function does not exist
-  for (it = cnx->bot->userFunctions.begin(); it !=
-         cnx->bot->userFunctions.end(); ++it)
-    if (newF == (*it)->name) {
+  if (cnx->bot->userFunctions[newF]) {
       from->sendNotice(newF + " \002is already an alias.\002");
       return;
-    }
+  }
 
   // Next, we check that the "old" function exist
-  bool found = false;
-  userFunction *u;
-  for (it = cnx->bot->userFunctions.begin(); it !=
-         cnx->bot->userFunctions.end(); ++it)
-    if (oldF == (*it)->name) {
-      found = true;
-      u = *it;
-      break;
+  if (!cnx->bot->userFunctions[oldF])
+    {
+      from->sendNotice(String("\002I don't know the\002 ") + oldF +
+                      " \002command.");
+      return;
     }
-  if (!found) {
-    from->sendNotice(String("\002I don't know the\002 ") + oldF +
-                     " \002command.");
-    return;
-  }
 
   // Fine, we do the binding
-  cnx->bot->userFunctions.push_back(new
-                               userFunction((char *)(const char 
-                                                            *)newF,
-                                                   u->function,
-                                                   u->minLevel,
-                                                   u->needsChannelName));
+  cnx->bot->userFunctions[newF] = 
+    new userFunction (*cnx->bot->userFunctions[oldF]);
 
   from->sendNotice("\002Alias added.\002");
 }
@@ -592,11 +578,11 @@ UserCommands::Help(ServerConnection *cnx, Person *from,
     int level = Utils::getLevel(cnx->bot, from->getAddress());
     String result = "";
     int length = 0;
-    std::list<userFunction *>::iterator it;
+    std::map<std::string, class userFunction*, std::less<std::string> >::iterator it;
     for (it = cnx->bot->userFunctions.begin(); it != cnx->bot->userFunctions.end(); ++it)
-      if ((*it)->minLevel <= level) {
-        result = result + (*it)->name + " ";
-        length += strlen((*it)->name) + 1;
+      if ((*it).second->minLevel <= level) {
+        result = result + (*it).first + " ";
+        length += (*it).first.length() + 1;
         if (length >= 256) {
           from->sendNotice(result);
           result = ""; length = 0;