%_load manual.var%
The Enterprise Edition of ConferenceRoom supports an interface to allow custom code to interface with the chat server directly. For UNIX platforms, the custom code must be supplied in the form of a shared object (.so) file. For WIN32 platforms, the custom code must be supplied in the form of a DLL file.
The following warnings should be kept in mind: Bugs in your custom code can destabilize the chat server or cause it to crash. ConferenceRoom is multithreaded and your code must be thread-safe. If your code is not re-entrant, you must protect it with mutexes or critical sections.
ConferenceRoom will call your code with as few locks as possible held. Normally ConferenceRoom will, at most, lock a single user's connection while your code is running. This enhances performance, but has some side-effects.
Suppose a user attempts to join a channel and you have hooked AuthenticeJoin. While your code is running, only that single user's connection is locked, so an operator could kill that user while your code is running. In this case, even if you return the correct code to let the user join the channel, the user will not wind up joining the channel since he's not on the server anymore.
On UNIX platforms, you may use C or C++ to create your code. On WIN32 platforms, you may use any language that makes it possible to create a DLL, but Visual C++ 5.0 or later is highly recommended.
You must command ConferenceRoom to separately hook each function you wish to intercept. If the same object file hooks more than one function, it will only be loaded/initalized once.
Callbacks are functions in CR that your code can call. Callouts are functions in your code that CR can call. Callouts must be configured in CR. You may use any callbacks you want without any special configuration.
You configure a callout with the command (from a chat client with administrative privileges):
/AS function <CallOutName> add <FileName>.<extension>
The file must be in a directory called lib under the master ConferenceRoom directory (the one with the bin directory under it. Note that if a function is already hooked (even if unsuccessful), you must unhook it first with:
/AS function <CallOutName> del
The following callbacks are currently defined:
DoDebug: This command adds an entry to ConferenceRoom's debug log. This is not normally useful except if you are trying to debug ConferenceRoom itself. Normally you should write debug information to your own file.
LockModule: Increments a module's usage count, preventing ConferenceRoom from unloading it. If you, for example, create your own thread, you should call this function because you do not want ConferenceRoom to unload your module while a thread is running in it -- that would be disastrous.
UnlockModule: Decrements your module's usage count.
GetTime: This gets ConferenceRoom's server time. This may differ from the system time for a variety of reasons. This time is guaranteed never to decrement and is guaranteed not to skip over any time values.
DoAS: This callback executes an AS command as if an administrator typed it in. You leave off the as part.
The following callouts are currently defined:
AuthenticateUser: This callout will be called every time a new user signs on to the server or a current local user changes nicknames. The connect/change can be allowed or disallowed and a message sent to the user.
AuthenticateJoin: This callout will be called every time a local user attempts to join a channel. The join can be allowed or disallowed and a message can be sent to the user.
DisconnectUser: This function allows a user who signs on to disconnect a previously-existing user with the same nickname.
Command: This callout will be called every time a client uses the 'CMD' command. It is used to provide a way to pass information from chat clients to custom code for logging or other purposes. A message can be returned in the form of a 780 numeric. This can be used in conjunction with the DoAS callback to provide for remotely controlled server configuration changes. This can also be used as a way of implementing custom commands or logging.
CreateChannel: This callout will be called every time a local client attempts to create a new channel (by joining an empty channel). The creation can be permitted or refused.
UserParted: This is a logging function that will be called to report that a local user has left a channel (by any means).
For more information, see the sample native interface code. If you don't have the sample native interface code, contact WebMaster Support. If you have any questions about the ConferenceRoom native code interface, please contact WebMaster Support at support@webmaster.com
#include <stddef.h> /* for size_t */
#include <string.h> /* for strcpy/strcasecmp */
#include <stdio.h> /* for sprintf */
/*
Example Native Code
By: David J. Schwartz <davids@webmaster.com>
Copyright (C) 1999-2000, WebMaster, Incorporated
$Id: lib.cpp,v 1.11 2001/08/08 18:37:36 cvs Exp $
On UNIX, compile with (may be different on your machine)
c++ test.cpp -c -DUNIX -o test.o -fPIC
(Some platforms may not need '-fPIC' or may want '-fpic')
Linux: ld -shared test.o -o test.so
Solaris: ld -G test.o -o test.so
(You can use a C or C++ compiler)
Then put in a 'lib' directory under the CR directory.
Then tell CR: /as function <FunctionName> add <FileName>.<so|dll>
On NT, compile/link into a DLL using whatever means you like.
All remotely accessible functions should have C linkage.
*/
#ifdef WIN32
#define strcasecmp stricmp
#if defined(__cplusplus)
#define EXPORT extern "C" __declspec(dllexport)
#define XPROTO __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#define XPROTO __declspec(dllexport)
#endif
#endif
#ifdef UNIX
#define EXPORT
#define XPROTO
#endif
#if defined(__cplusplus)
extern "C" {
#endif
XPROTO void RegisterCallbackHandler(void *[]);
XPROTO int AuthenticateUser(const char *, const char *,
const char *, const char *, char *, size_t);
XPROTO int DisconnectUser(const char *, const char *,
const char *, char *, size_t);
XPROTO int Command(const char *, const char *, const char *,
const char *, const char *, const char *, char *, size_t);
XPROTO int AuthenticateJoin(unsigned, const char *, const char *,
const char *, const char *, const char *, char *, size_t);
XPROTO int CreateChannel(const char *, const char *, const char *,
const char *, const char *, const char *, char *, size_t);
XPROTO int UserDisconnected(const char *, const char *, const char *,
const char *, const char *, int);
XPROTO int UserParted(const char *, const char *, const char *,
const char *, const char *, int);
void (*CallBackRoutine)(int, void *[]);
#if defined(__cplusplus)
};
#endif
/*
CR gives us a module handle that we use to uniquely identify ourself.
It's needed so that CR knows where callbacks are coming from.
*/
void *Handle;
/*
DoDebug is an example of a routine that calls back into CR.
All callbacks have numeric indexes.
The callback handler is thread safe as well, so you can start your
own threads and make calls back into CR willy-nilly. However, you will
have to register each thread with CR and clean it up when done. (So CR
can adjust the 'usage count' of the library and not unload it while
it's got a thread in its code!)
Every callback function takes an array of void pointers. The first
pointer on input must always be the module handle.
*/
void DoDebug(const char *msg)
{ /* Do not modify this function */
void *a[2];
a[0]=Handle;
a[1]=(void *) msg;
CallBackRoutine(-4, a);
}
/*
When you create a thread or otherwise make your module 'busy', you
must call 'LockModule' to warn CR not to unload the module. When you
are done, you must call 'UnlockModule'. Each call to 'LockModule'
increments the usage count of this module by one. Misuse of these
functions WILL cause CR to crash.
*/
void LockModule(void)
{ /* Do not modify this function */
void *a[1];
a[0]=Handle;
CallBackRoutine(-9, a);
}
void UnlockModule(void)
{ /* Do not modify this function */
void *a[1];
a[0]=Handle;
CallBackRoutine(-10, a);
}
/*
This function gets CR's internal time. CR's internal time may not match
the system time.
*/
time_t GetTime(void)
{ /* Do not modify this function */
void *a[2];
a[0]=Handle;
CallBackRoutine(-7, a);
return (time_t) a[1];
}
/*
DoAS is a callback routine that allows you to execute an 'AS' command.
The return value indicates the success or failure of the command. If the
'reply' buffer is large enough, CR's response to the command will be
copied into it.
*/
int DoAS(const char *cmd, char *reply, int buf_siz)
{ /* Do not modify this function */
void *a[4];
a[0]=Handle;
a[1]=(void *) cmd;
CallBackRoutine(1, a);
char *r=(char *) a[3];
if(strlen(r)<buf_siz) strcpy(reply, r);
free(a[3]);
return (int) a[2];
}
/*
CR calls RegisterCallbackHandler when it loads a library
to tell the library its library handle and give it a pointer
to CR's callback handler.
*/
EXPORT void RegisterCallbackHandler(void *a[])
{
/* Save the information we've been given */
CallBackRoutine=(void (*)(int, void *[])) a[0];
Handle=a[1];
/*
Put any initialization code you might need here
You are guaranteed that this function will be called once
and only once upon module loading
*/
/* The following line just lets us know that we've been loaded */
/* It will appear in CR's debug log */
DoDebug("We've been hooked");
}
/*
This is a silly version of the AuthenticateUser function just to
test. It disallows all nick changes except for case changes and
refuses to permit nicknames with 'reject' in them.
CR will call the AuthenticateUser hook any time a local user
signs on to the server or changes nicknames.
*/
EXPORT int AuthenticateUser(const char *oldnick, const char *newnick,
const char *username, const char *password, char *replybuf, size_t buflen)
{
DoDebug("AuthenticateUser has been called");
if(oldnick==NULL)
{ /* oldnick is NULL if this is a user signing on */
DoDebug("It's a new nickname");
if(strstr(newnick,"reject")!=NULL)
{
DoDebug("Nickname contains 'reject', rejecting");
if(buflen>40) strcpy(replybuf,"Don't use 'reject' in a nickname");
return 1;
}
DoDebug("Allowing");
return 0; /* allow */
}
/* if newnick is not NULL, this is a nick change */
if(strncmp(oldnick,"Guest",5)==0)
{
DoDebug("Allowing change from guest");
return 0; /* allow change from guest */
}
if(strcasecmp(oldnick,newnick)!=0)
{
DoDebug("Not allowing nick change");
if(buflen>43) strcpy(replybuf,"I hate nick changes");
return 1; /* deny */
}
DoDebug("Changes only case, allowing");
return 0; /* allow */
}
/*
The Command hook is used to pass information from a chat client to
customized code. It also allows the customized code to send a reply
back to the client. It can be used to add features to CR.
The example code belows just tells the user what he sent.
*/
EXPORT int Command(const char *nick, const char *user, const char *host,
const char *realname, const char *server, const char *command,
char *replybuf, size_t buflen)
{
DoDebug("Command has been called");
char buf[512];
sprintf(buf,"n=%s u=%s h=%s rn=%s srv=%s cmd=%s",
nick,user,host,realname,server,command);
strcpy(replybuf, buf); /* Squirt back our parameters */
return 1; /* 1 = send message */
}
/*
The AuthenticateJoin function is used to permit or refuse an attempt
to join a channel. The 'type' field consists of the bitwise OR of these
flags:
1 - SAJOIN
2 - User is +o
4 - User is +h
8 - Channel was created by the join
And the permitted return values are the bitwise OR of these flags:
1 - Allow join
2 - Set +u in channel
4 - Set +v in channel
8 - Set +o in channel
Currently, the reply message is sent only if the join is refused.
The 'password' is the most recent password sent using the 'PASS'
command. The 'join_key' is the last parameter to the JOIN command
and is used to get into keyed channels.
*/
EXPORT int AuthenticateJoin(unsigned type, const char *nickname,
const char *username, const char *password,
const char *channel, const char *join_key,
char *replybuf, size_t buflen)
{
if(type & 8) return 8; /* If creating channel, op */
return 1;
}
/*
The DisconnectUser function is called when a user signs on with
a nickname that is already is use. If you can establish that this
newly connecting user has authoritative rights to the nickname,
you can return a '1', and the old user will be killed and the new
user allowed to use the nickname.
*/
EXPORT int DisconnectUser(cosnt char *nick, const char *user,
const char *pass, char *replybuf, size_t buflen)
{
return 0; /* 1 = disconnect existing user */
}
/*
This function will be called any time a user attempts to create
a channel (by joining an empty one). Returning a zero permits the
channel to be created. If you don't put a message in the replybuf,
a NOSUCHCHANNEL error numeric will be sent.
*/
EXPORT int CreateChannel(const char *channel, const char *nickname,
const char *username, const char *hostname, const char *password,
const char *join_key, char *replybuf, size_t buflen)
{
return 0;
}
/*
This function will be called any time a user disconnects.
It is intended to be used for logging. The return value is ignored.
The 'code' means:
0 = normal close, 1 = timeout (for HTML/Envoy clients)
2 = deconfigured (for bots), 3 = lost in split
4 = disconnect requested for unspecified reason
5 = quit command, 6 = z-lined while connected
7 = mkill command, 8 = kill command
9 = DisconnectUser hook requested disconnect
10 = K-lined, 11 = collision, 12 = ping timeout
13 = unknown (code last track of object)
14 = type mismatch (expected server, outbound connection)
15 = banned, 16 = no privileges (no client class)
17 = client class full
19 = client class doesn't allow clones
20 = AuthenticateUser hook refused connection
21 = user squit himself, 22 = excess flood
99 = sendq not draining, 100 = normal close
>100 = system error, subtract 100 for code
*/
EXPORT int UserDisconnected(const char *nickname, const char *username,
const char *hostname, const char *realname, const char *reason, int code)
{
return 0;
}
/*
This function will be called any time a user parts a channel.
It is intended to be used for logging. The return value is ignored.
The 'code' means: 0=PARTed, 1=QUIT/error, 2=KICKed, 3=KILLed
*/
EXPORT int UserParted(const char *nickname, const char *username,
const char *hostname, const char *channel, const char *reason, int code)
{
return 0;
}