mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-03 20:43:12 +02:00
Merge branch 'unreal60_dev' of github.com:syzop/unrealircd-next into unreal60_dev
This commit is contained in:
+8
-1
@@ -309,7 +309,8 @@ DLL_FILES=SRC/MODULES/CLOAK.DLL \
|
||||
SRC/MODULES/CHATHISTORY.DLL \
|
||||
SRC/MODULES/TARGETFLOODPROT.DLL \
|
||||
SRC/MODULES/TYPING-INDICATOR.DLL \
|
||||
SRC/MODULES/CLIENTTAGDENY.DLL
|
||||
SRC/MODULES/CLIENTTAGDENY.DLL \
|
||||
SRC/MODULES/WATCH-BACKEND.DLL
|
||||
|
||||
|
||||
ALL: CONF UNREALSVC.EXE UnrealIRCd.exe MODULES
|
||||
@@ -1126,5 +1127,11 @@ src/modules/typing-indicator.dll: src/modules/typing-indicator.c $(INCLUDES)
|
||||
|
||||
src/modules/clienttagdeny.dll: src/modules/clienttagdeny.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/clienttagdeny.c $(MODLFLAGS)
|
||||
|
||||
src/modules/watch-backend.dll: src/modules/watch-backend.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/watch-backend.c $(MODLFLAGS)
|
||||
|
||||
src/modules/monitor.dll: src/modules/monitor.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/monitor.c $(MODLFLAGS)
|
||||
|
||||
dummy:
|
||||
|
||||
@@ -213,6 +213,7 @@ loadmodule "sts"; /* strict transport policy (set::tls::sts-policy) */
|
||||
loadmodule "link-security"; /* link-security announce */
|
||||
loadmodule "plaintext-policy"; /* plaintext-policy announce */
|
||||
loadmodule "chathistory"; /* CHATHISTORY client command, 005 and a CAP (draft) */
|
||||
loadmodule "monitor"; /* MONITOR command with functionality similar to WATCH */
|
||||
|
||||
|
||||
/*** Other ***/
|
||||
@@ -235,3 +236,4 @@ loadmodule "connthrottle"; /* see https://www.unrealircd.org/docs/Connthrottle *
|
||||
loadmodule "userip-tag"; /* unrealircd.org/userip tag for ircops */
|
||||
loadmodule "userhost-tag"; /* unrealircd.org/userhost tag for ircops */
|
||||
loadmodule "targetfloodprot"; /* set::anti-flood::target-flood protection */
|
||||
loadmodule "watch-backend"; /* used by watch and other modules */
|
||||
|
||||
+5
-7
@@ -336,7 +336,6 @@ extern void del_queries(char *);
|
||||
/* Hash stuff */
|
||||
#define NICK_HASH_TABLE_SIZE 32768
|
||||
#define CHAN_HASH_TABLE_SIZE 32768
|
||||
#define WATCH_HASH_TABLE_SIZE 32768
|
||||
#define WHOWAS_HASH_TABLE_SIZE 32768
|
||||
#define THROTTLING_HASH_TABLE_SIZE 8192
|
||||
extern uint64_t siphash(const char *in, const char *k);
|
||||
@@ -351,12 +350,6 @@ extern int add_to_id_hash_table(char *, Client *);
|
||||
extern int del_from_id_hash_table(char *, Client *);
|
||||
extern int add_to_channel_hash_table(char *, Channel *);
|
||||
extern void del_from_channel_hash_table(char *, Channel *);
|
||||
extern int add_to_watch_hash_table(char *, Client *, int);
|
||||
extern int del_from_watch_hash_table(char *, Client *);
|
||||
extern int hash_check_watch(Client *, int);
|
||||
extern int hash_del_watch_list(Client *);
|
||||
extern void count_watch_memory(int *, u_long *);
|
||||
extern Watch *hash_get_watch(char *);
|
||||
extern Channel *hash_get_chan_bucket(uint64_t);
|
||||
extern Client *hash_find_client(const char *, Client *);
|
||||
extern Client *hash_find_id(const char *, Client *);
|
||||
@@ -777,6 +770,11 @@ extern MODVAR void *(*labeled_response_save_context)(void);
|
||||
extern MODVAR void (*labeled_response_set_context)(void *ctx);
|
||||
extern MODVAR void (*labeled_response_force_end)(void);
|
||||
extern MODVAR void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment);
|
||||
extern MODVAR int (*watch_add)(char *nick, Client *client, int flags);
|
||||
extern MODVAR int (*watch_del)(char *nick, Client *client, int flags);
|
||||
extern MODVAR int (*watch_del_list)(Client *client, int flags);
|
||||
extern MODVAR Watch *(*watch_get)(char *nick);
|
||||
extern MODVAR int (*watch_check)(Client *client, int reply);
|
||||
extern MODVAR char *(*tkl_uhost)(TKL *tkl, char *buf, size_t buflen, int options);
|
||||
/* /Efuncs */
|
||||
|
||||
|
||||
+41
-3
@@ -24,7 +24,7 @@
|
||||
#define MAXCUSTOMHOOKS 30
|
||||
#define MAXHOOKTYPES 150
|
||||
#define MAXCALLBACKS 30
|
||||
#define MAXEFUNCTIONS 90
|
||||
#define MAXEFUNCTIONS 128
|
||||
#if defined(_WIN32)
|
||||
#define MOD_EXTENSION "dll"
|
||||
#define DLLFUNC _declspec(dllexport)
|
||||
@@ -1165,6 +1165,12 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va
|
||||
#define HOOKTYPE_CONNECT_EXTINFO 104
|
||||
/** See hooktype_is_invited() */
|
||||
#define HOOKTYPE_IS_INVITED 105
|
||||
/** See hooktype_post_local_nickchange() */
|
||||
#define HOOKTYPE_POST_LOCAL_NICKCHANGE 106
|
||||
/** See hooktype_post_remote_nickchange() */
|
||||
#define HOOKTYPE_POST_REMOTE_NICKCHANGE 107
|
||||
/** See hooktype_watch_notify() */
|
||||
#define HOOKTYPE_WATCH_NOTIFICATION 108
|
||||
/* Adding a new hook here?
|
||||
* 1) Add the #define HOOKTYPE_.... with a new number
|
||||
* 2) Add a hook prototype (see below)
|
||||
@@ -1523,9 +1529,10 @@ int hooktype_modechar_add(Channel *channel, int modechar);
|
||||
* @param client The client
|
||||
* @param mtags Message tags associated with the event
|
||||
* @param reason The away reason, or NULL if away is unset.
|
||||
* @param already_as_away Set to 1 if the user only changed their away reason.
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_away(Client *client, MessageTag *mtags, char *reason);
|
||||
int hooktype_away(Client *client, MessageTag *mtags, char *reason, int already_as_away);
|
||||
|
||||
/** Called when a user wants to invite another user to a channel (function prototype for HOOKTYPE_PRE_INVITE).
|
||||
* @param client The client
|
||||
@@ -2114,6 +2121,29 @@ int hooktype_connect_extinfo(Client *client, NameValuePrioList **list);
|
||||
*/
|
||||
int hooktype_is_invited(Client *client, Channel *channel, int *invited);
|
||||
|
||||
/** Called after a local user has changed the nick name (function prototype for HOOKTYPE_POST_LOCAL_NICKCHANGE).
|
||||
* @param client The client
|
||||
* @param mtags Message tags associated with the event
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_post_local_nickchange(Client *client, MessageTag *mtags);
|
||||
|
||||
/** Called after a remote user has changed the nick name (function prototype for HOOKTYPE_POST_REMOTE_NICKCHANGE).
|
||||
* @param client The client
|
||||
* @param mtags Message tags associated with the event
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_post_remote_nickchange(Client *client, MessageTag *mtags);
|
||||
|
||||
/** Called when a user changed its state in a way that should trigger a WATCH notification (function prototype for HOOKTYPE_WATCH_NOTIFY).
|
||||
* @param client The client whose state has changed
|
||||
* @param watch The watch list entry
|
||||
* @param lp The associated watch list entry for WATCHing user
|
||||
* @param reply The numeric that is supposed to be sent as a notification (module-defined)
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_watch_notification(Client *client, Watch *watch, Link *lp, int reply);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef GCC_TYPECHECKING
|
||||
@@ -2224,7 +2254,10 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
|
||||
((hooktype == HOOKTYPE_ACCOUNT_LOGIN) && !ValidateHook(hooktype_account_login, func)) || \
|
||||
((hooktype == HOOKTYPE_CLOSE_CONNECTION) && !ValidateHook(hooktype_close_connection, func)) || \
|
||||
((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, func)) || \
|
||||
((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) ) \
|
||||
((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) || \
|
||||
((hooktype == HOOKTYPE_POST_LOCAL_NICKCHANGE) && !ValidateHook(hooktype_post_local_nickchange, func)) || \
|
||||
((hooktype == HOOKTYPE_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) || \
|
||||
((hooktype == HOOKTYPE_WATCH_NOTIFICATION) && !ValidateHook(hooktype_watch_notification, func)) )\
|
||||
_hook_error_incompatible();
|
||||
#endif /* GCC_TYPECHECKING */
|
||||
|
||||
@@ -2335,6 +2368,11 @@ enum EfunctionType {
|
||||
EFUNC_LABELED_RESPONSE_SET_CONTEXT,
|
||||
EFUNC_LABELED_RESPONSE_FORCE_END,
|
||||
EFUNC_KICK_USER,
|
||||
EFUNC_WATCH_ADD,
|
||||
EFUNC_WATCH_DEL,
|
||||
EFUNC_WATCH_DEL_LIST,
|
||||
EFUNC_WATCH_GET,
|
||||
EFUNC_WATCH_CHECK,
|
||||
EFUNC_TKL_UHOST,
|
||||
};
|
||||
|
||||
|
||||
@@ -375,6 +375,12 @@
|
||||
|
||||
#define RPL_WHOISSECURE 671
|
||||
|
||||
#define RPL_MONONLINE 730
|
||||
#define RPL_MONOFFLINE 731
|
||||
#define RPL_MONLIST 732
|
||||
#define RPL_ENDOFMONLIST 733
|
||||
#define ERR_MONLISTFULL 734
|
||||
|
||||
#define ERR_MLOCKRESTRICTED 742
|
||||
|
||||
#define ERR_CANNOTDOCOMMAND 972
|
||||
|
||||
+9
-2
@@ -1353,8 +1353,6 @@ struct LocalClient {
|
||||
u_short sendB; /**< Statistics: counters to count upto 1-k lots of bytes */
|
||||
u_short receiveB; /**< Statistics: sent and received (???) */
|
||||
short lastsq; /**< # of 2k blocks when sendqueued called last */
|
||||
Link *watch; /**< Watch notification list (WATCH) for this user */
|
||||
u_short watches; /**< Number of entries in the watch list */
|
||||
ModData moddata[MODDATA_MAX_LOCAL_CLIENT]; /**< LocalClient attached module data, used by the ModData system */
|
||||
#ifdef DEBUGMODE
|
||||
time_t cputime; /**< Something with debugging (why is this a time_t? TODO) */
|
||||
@@ -2001,6 +1999,15 @@ struct Mode {
|
||||
char key[KEYLEN + 1]; /**< The +k key in effect (eg: secret), if any - otherwise NULL */
|
||||
};
|
||||
|
||||
/* flags for Link if used to contain Watch --k4be */
|
||||
|
||||
/* WATCH type */
|
||||
#define WATCH_FLAG_TYPE_WATCH (1<<0) /* added via /WATCH command */
|
||||
#define WATCH_FLAG_TYPE_MONITOR (1<<1) /* added via /MONITOR command */
|
||||
|
||||
/* behaviour switches */
|
||||
#define WATCH_FLAG_AWAYNOTIFY (1<<8) /* should send AWAY notifications */
|
||||
|
||||
/* Used for notify-hash buckets... -Donwulff */
|
||||
|
||||
struct Watch {
|
||||
|
||||
@@ -123,6 +123,11 @@ void *(*labeled_response_save_context)(void);
|
||||
void (*labeled_response_set_context)(void *ctx);
|
||||
void (*labeled_response_force_end)(void);
|
||||
void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment);
|
||||
int (*watch_add)(char *nick, Client *client, int flags);
|
||||
int (*watch_del)(char *nick, Client *client, int flags);
|
||||
int (*watch_del_list)(Client *client, int flags);
|
||||
Watch *(*watch_get)(char *nick);
|
||||
int (*watch_check)(Client *client, int reply);
|
||||
|
||||
Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*cfunc)())
|
||||
{
|
||||
@@ -279,6 +284,12 @@ void efunctions_switchover(void)
|
||||
|
||||
void efunc_init_function_(EfunctionType what, char *name, void *func, void *default_func)
|
||||
{
|
||||
if (what >= MAXEFUNCTIONS)
|
||||
{
|
||||
/* increase MAXEFUNCTIONS if you ever encounter that --k4be */
|
||||
ircd_log(LOG_ERROR, "Too many efunctions!");
|
||||
abort();
|
||||
}
|
||||
safe_strdup(efunction_table[what].name, name);
|
||||
efunction_table[what].funcptr = func;
|
||||
efunction_table[what].deffunc = default_func;
|
||||
@@ -368,5 +379,11 @@ void efunctions_init(void)
|
||||
efunc_init_function(EFUNC_LABELED_RESPONSE_SET_CONTEXT, labeled_response_set_context, labeled_response_set_context_default_handler);
|
||||
efunc_init_function(EFUNC_LABELED_RESPONSE_FORCE_END, labeled_response_force_end, labeled_response_force_end_default_handler);
|
||||
efunc_init_function(EFUNC_KICK_USER, kick_user, NULL);
|
||||
efunc_init_function(EFUNC_WATCH_ADD, watch_add, NULL);
|
||||
efunc_init_function(EFUNC_WATCH_DEL, watch_del, NULL);
|
||||
efunc_init_function(EFUNC_WATCH_DEL_LIST, watch_del_list, NULL);
|
||||
efunc_init_function(EFUNC_WATCH_GET, watch_get, NULL);
|
||||
efunc_init_function(EFUNC_WATCH_CHECK, watch_check, NULL);
|
||||
efunc_init_function(EFUNC_TKL_UHOST, tkl_uhost, NULL);
|
||||
}
|
||||
|
||||
|
||||
-306
@@ -260,11 +260,9 @@ void siphash_generate_key(char *k)
|
||||
static struct list_head clientTable[NICK_HASH_TABLE_SIZE];
|
||||
static struct list_head idTable[NICK_HASH_TABLE_SIZE];
|
||||
static Channel *channelTable[CHAN_HASH_TABLE_SIZE];
|
||||
static Watch *watchTable[WATCH_HASH_TABLE_SIZE];
|
||||
|
||||
static char siphashkey_nick[SIPHASH_KEY_LENGTH];
|
||||
static char siphashkey_chan[SIPHASH_KEY_LENGTH];
|
||||
static char siphashkey_watch[SIPHASH_KEY_LENGTH];
|
||||
static char siphashkey_whowas[SIPHASH_KEY_LENGTH];
|
||||
static char siphashkey_throttling[SIPHASH_KEY_LENGTH];
|
||||
|
||||
@@ -277,7 +275,6 @@ void init_hash(void)
|
||||
|
||||
siphash_generate_key(siphashkey_nick);
|
||||
siphash_generate_key(siphashkey_chan);
|
||||
siphash_generate_key(siphashkey_watch);
|
||||
siphash_generate_key(siphashkey_whowas);
|
||||
siphash_generate_key(siphashkey_throttling);
|
||||
|
||||
@@ -288,7 +285,6 @@ void init_hash(void)
|
||||
INIT_LIST_HEAD(&idTable[i]);
|
||||
|
||||
memset(channelTable, 0, sizeof(channelTable));
|
||||
memset(watchTable, 0, sizeof(watchTable));
|
||||
|
||||
memset(ThrottlingHash, 0, sizeof(ThrottlingHash));
|
||||
/* do not call init_throttling() here, as
|
||||
@@ -310,11 +306,6 @@ uint64_t hash_channel_name(const char *name)
|
||||
return siphash_nocase(name, siphashkey_chan) % CHAN_HASH_TABLE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t hash_watch_nick_name(const char *name)
|
||||
{
|
||||
return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
|
||||
}
|
||||
|
||||
uint64_t hash_whowas_name(const char *name)
|
||||
{
|
||||
return siphash_nocase(name, siphashkey_whowas) % WHOWAS_HASH_TABLE_SIZE;
|
||||
@@ -597,303 +588,6 @@ Channel *hash_get_chan_bucket(uint64_t hashv)
|
||||
return channelTable[hashv];
|
||||
}
|
||||
|
||||
void count_watch_memory(int *count, u_long *memory)
|
||||
{
|
||||
int i = WATCH_HASH_TABLE_SIZE;
|
||||
Watch *anptr;
|
||||
|
||||
while (i--)
|
||||
{
|
||||
anptr = watchTable[i];
|
||||
while (anptr)
|
||||
{
|
||||
(*count)++;
|
||||
(*memory) += sizeof(Watch)+strlen(anptr->nick);
|
||||
anptr = anptr->hnext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_to_watch_hash_table
|
||||
*/
|
||||
int add_to_watch_hash_table(char *nick, Client *client, int awaynotify)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *anptr;
|
||||
Link *lp;
|
||||
|
||||
|
||||
/* Get the right bucket... */
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
/* Find the right nick (header) in the bucket, or NULL... */
|
||||
if ((anptr = (Watch *)watchTable[hashv]))
|
||||
while (anptr && mycmp(anptr->nick, nick))
|
||||
anptr = anptr->hnext;
|
||||
|
||||
/* If found NULL (no header for this nick), make one... */
|
||||
if (!anptr) {
|
||||
anptr = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick));
|
||||
anptr->lasttime = timeofday;
|
||||
strcpy(anptr->nick, nick);
|
||||
|
||||
anptr->watch = NULL;
|
||||
|
||||
anptr->hnext = watchTable[hashv];
|
||||
watchTable[hashv] = anptr;
|
||||
}
|
||||
/* Is this client already on the watch-list? */
|
||||
if ((lp = anptr->watch))
|
||||
while (lp && (lp->value.client != client))
|
||||
lp = lp->next;
|
||||
|
||||
/* No it isn't, so add it in the bucket and client addint it */
|
||||
if (!lp) {
|
||||
lp = anptr->watch;
|
||||
anptr->watch = make_link();
|
||||
anptr->watch->value.client = client;
|
||||
anptr->watch->flags = awaynotify;
|
||||
anptr->watch->next = lp;
|
||||
|
||||
lp = make_link();
|
||||
lp->next = client->local->watch;
|
||||
lp->value.wptr = anptr;
|
||||
lp->flags = awaynotify;
|
||||
client->local->watch = lp;
|
||||
client->local->watches++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_check_watch
|
||||
*/
|
||||
int hash_check_watch(Client *client, int reply)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *anptr;
|
||||
Link *lp;
|
||||
int awaynotify = 0;
|
||||
|
||||
if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY))
|
||||
awaynotify = 1;
|
||||
|
||||
/* Get us the right bucket */
|
||||
hashv = hash_watch_nick_name(client->name);
|
||||
|
||||
/* Find the right header in this bucket */
|
||||
if ((anptr = (Watch *)watchTable[hashv]))
|
||||
while (anptr && mycmp(anptr->nick, client->name))
|
||||
anptr = anptr->hnext;
|
||||
if (!anptr)
|
||||
return 0; /* This nick isn't on watch */
|
||||
|
||||
/* Update the time of last change to item */
|
||||
anptr->lasttime = TStime();
|
||||
|
||||
/* Send notifies out to everybody on the list in header */
|
||||
for (lp = anptr->watch; lp; lp = lp->next)
|
||||
{
|
||||
if (!awaynotify)
|
||||
{
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), anptr->lasttime, client->info);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* AWAY or UNAWAY */
|
||||
if (!lp->flags)
|
||||
continue; /* skip away/unaway notification for users not interested in them */
|
||||
|
||||
if (reply == RPL_NOTAWAY)
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), client->user->lastaway);
|
||||
else /* RPL_GONEAWAY / RPL_REAWAY */
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), client->user->lastaway, client->user->away);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_get_watch
|
||||
*/
|
||||
Watch *hash_get_watch(char *nick)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *anptr;
|
||||
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
if ((anptr = (Watch *)watchTable[hashv]))
|
||||
while (anptr && mycmp(anptr->nick, nick))
|
||||
anptr = anptr->hnext;
|
||||
|
||||
return anptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* del_from_watch_hash_table
|
||||
*/
|
||||
int del_from_watch_hash_table(char *nick, Client *client)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *anptr, *nlast = NULL;
|
||||
Link *lp, *last = NULL;
|
||||
|
||||
/* Get the bucket for this nick... */
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
/* Find the right header, maintaining last-link pointer... */
|
||||
if ((anptr = (Watch *)watchTable[hashv]))
|
||||
while (anptr && mycmp(anptr->nick, nick)) {
|
||||
nlast = anptr;
|
||||
anptr = anptr->hnext;
|
||||
}
|
||||
if (!anptr)
|
||||
return 0; /* No such watch */
|
||||
|
||||
/* Find this client from the list of notifies... with last-ptr. */
|
||||
if ((lp = anptr->watch))
|
||||
while (lp && (lp->value.client != client)) {
|
||||
last = lp;
|
||||
lp = lp->next;
|
||||
}
|
||||
if (!lp)
|
||||
return 0; /* No such client to watch */
|
||||
|
||||
/* Fix the linked list under header, then remove the watch entry */
|
||||
if (!last)
|
||||
anptr->watch = lp->next;
|
||||
else
|
||||
last->next = lp->next;
|
||||
free_link(lp);
|
||||
|
||||
/* Do the same regarding the links in client-record... */
|
||||
last = NULL;
|
||||
if ((lp = client->local->watch))
|
||||
while (lp && (lp->value.wptr != anptr)) {
|
||||
last = lp;
|
||||
lp = lp->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give error on the odd case... probobly not even neccessary
|
||||
* No error checking in ircd is unneccessary ;) -Cabal95
|
||||
*/
|
||||
if (!lp)
|
||||
sendto_ops("WATCH debug error: del_from_watch_hash_table "
|
||||
"found a watch entry with no client "
|
||||
"counterpoint processing nick %s on client %p!",
|
||||
nick, client->user);
|
||||
else {
|
||||
if (!last) /* First one matched */
|
||||
client->local->watch = lp->next;
|
||||
else
|
||||
last->next = lp->next;
|
||||
free_link(lp);
|
||||
}
|
||||
/* In case this header is now empty of notices, remove it */
|
||||
if (!anptr->watch) {
|
||||
if (!nlast)
|
||||
watchTable[hashv] = anptr->hnext;
|
||||
else
|
||||
nlast->hnext = anptr->hnext;
|
||||
safe_free(anptr);
|
||||
}
|
||||
|
||||
/* Update count of notifies on nick */
|
||||
client->local->watches--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_del_watch_list
|
||||
*/
|
||||
int hash_del_watch_list(Client *client)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *anptr;
|
||||
Link *np, *lp, *last;
|
||||
|
||||
|
||||
if (!(np = client->local->watch))
|
||||
return 0; /* Nothing to do */
|
||||
|
||||
client->local->watch = NULL; /* Break the watch-list for client */
|
||||
while (np) {
|
||||
/* Find the watch-record from hash-table... */
|
||||
anptr = np->value.wptr;
|
||||
last = NULL;
|
||||
for (lp = anptr->watch; lp && (lp->value.client != client);
|
||||
lp = lp->next)
|
||||
last = lp;
|
||||
|
||||
/* Not found, another "worst case" debug error */
|
||||
if (!lp)
|
||||
sendto_ops("WATCH Debug error: hash_del_watch_list "
|
||||
"found a WATCH entry with no table "
|
||||
"counterpoint processing client %s!",
|
||||
client->name);
|
||||
else {
|
||||
/* Fix the watch-list and remove entry */
|
||||
if (!last)
|
||||
anptr->watch = lp->next;
|
||||
else
|
||||
last->next = lp->next;
|
||||
free_link(lp);
|
||||
|
||||
/*
|
||||
* If this leaves a header without notifies,
|
||||
* remove it. Need to find the last-pointer!
|
||||
*/
|
||||
if (!anptr->watch) {
|
||||
Watch *np2, *nl;
|
||||
|
||||
hashv = hash_watch_nick_name(anptr->nick);
|
||||
|
||||
nl = NULL;
|
||||
np2 = watchTable[hashv];
|
||||
while (np2 != anptr) {
|
||||
nl = np2;
|
||||
np2 = np2->hnext;
|
||||
}
|
||||
|
||||
if (nl)
|
||||
nl->hnext = anptr->hnext;
|
||||
else
|
||||
watchTable[hashv] = anptr->hnext;
|
||||
safe_free(anptr);
|
||||
}
|
||||
}
|
||||
|
||||
lp = np; /* Save last pointer processed */
|
||||
np = np->next; /* Jump to the next pointer */
|
||||
free_link(lp); /* Free the previous */
|
||||
}
|
||||
|
||||
client->local->watches = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Throttling - originally by Stskeeps */
|
||||
|
||||
/* Note that we call this set::anti-flood::connect-flood nowadays */
|
||||
|
||||
+1
-4
@@ -543,8 +543,6 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com
|
||||
}
|
||||
if (*client->name)
|
||||
del_from_client_hash_table(client->name, client);
|
||||
if (IsUser(client))
|
||||
hash_check_watch(client, RPL_LOGOFF);
|
||||
if (remote_rehash_client == client)
|
||||
remote_rehash_client = NULL; /* client did a /REHASH and QUIT before rehash was complete */
|
||||
remove_client_from_list(client);
|
||||
@@ -634,8 +632,7 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char
|
||||
log_data_string("extended_client_info", get_connect_extinfo(client)),
|
||||
log_data_string("reason", comment),
|
||||
log_data_integer("connected_time", connected_time));
|
||||
/* Clean out list and watch structures -Donwulff */
|
||||
hash_del_watch_list(client);
|
||||
|
||||
} else
|
||||
if (IsUnknown(client))
|
||||
{
|
||||
|
||||
+10
-1
@@ -74,7 +74,8 @@ R_MODULES= \
|
||||
bot-tag.so \
|
||||
reply-tag.so typing-indicator.so \
|
||||
ident_lookup.so history.so chathistory.so \
|
||||
targetfloodprot.so clienttagdeny.so
|
||||
targetfloodprot.so clienttagdeny.so watch-backend.so \
|
||||
monitor.so
|
||||
|
||||
MODULES=cloak.so $(R_MODULES)
|
||||
MODULEFLAGS=@MODULEFLAGS@
|
||||
@@ -653,6 +654,14 @@ clienttagdeny.so: clienttagdeny.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o clienttagdeny.so clienttagdeny.c
|
||||
|
||||
watch-backend.so: watch-backend.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o watch-backend.so watch-backend.c
|
||||
|
||||
monitor.so: monitor.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o monitor.so monitor.c
|
||||
|
||||
#############################################################################
|
||||
# capabilities
|
||||
#############################################################################
|
||||
|
||||
+2
-5
@@ -73,10 +73,9 @@ CMD_FUNC(cmd_away)
|
||||
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
sendto_server(client, 0, 0, mtags, ":%s AWAY", client->name);
|
||||
hash_check_watch(client, RPL_NOTAWAY);
|
||||
sendto_local_common_channels(client, client, ClientCapabilityBit("away-notify"), mtags,
|
||||
":%s AWAY", client->name);
|
||||
RunHook3(HOOKTYPE_AWAY, client, mtags, NULL);
|
||||
RunHook4(HOOKTYPE_AWAY, client, mtags, NULL, 0);
|
||||
free_message_tags(mtags);
|
||||
}
|
||||
|
||||
@@ -125,13 +124,11 @@ CMD_FUNC(cmd_away)
|
||||
if (MyConnect(client))
|
||||
sendnumeric(client, RPL_NOWAWAY);
|
||||
|
||||
hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY);
|
||||
|
||||
sendto_local_common_channels(client, client,
|
||||
ClientCapabilityBit("away-notify"), mtags,
|
||||
":%s AWAY :%s", client->name, client->user->away);
|
||||
|
||||
RunHook3(HOOKTYPE_AWAY, client, mtags, client->user->away);
|
||||
RunHook4(HOOKTYPE_AWAY, client, mtags, client->user->away, already_as_away);
|
||||
|
||||
free_message_tags(mtags);
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* IRC - Internet Relay Chat, src/modules/monitor.c
|
||||
* (C) 2021 The UnrealIRCd Team
|
||||
*
|
||||
* See file AUTHORS in IRC package for additional names of
|
||||
* the programmers.
|
||||
*
|
||||
* 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 1, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
#define MSG_MONITOR "MONITOR"
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
CMD_FUNC(cmd_monitor);
|
||||
char *monitor_isupport_param(void);
|
||||
int monitor_nickchange(Client *client, MessageTag *mtags, char *newnick);
|
||||
int monitor_post_nickchange(Client *client, MessageTag *mtags);
|
||||
int monitor_quit(Client *client, MessageTag *mtags, char *comment);
|
||||
int monitor_connect(Client *client);
|
||||
int monitor_notification(Client *client, Watch *watch, Link *lp, int reply);
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"monitor",
|
||||
"5.0",
|
||||
"command /monitor",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
CommandAdd(modinfo->handle, MSG_MONITOR, cmd_monitor, 2, CMD_USER);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, monitor_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, monitor_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_POST_LOCAL_NICKCHANGE, 0, monitor_post_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_POST_REMOTE_NICKCHANGE, 0, monitor_post_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, monitor_quit);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, monitor_quit);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, monitor_connect);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, monitor_connect);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_WATCH_NOTIFICATION, 0, monitor_notification);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
ISupportAdd(modinfo->handle, "MONITOR", monitor_isupport_param());
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
char *monitor_isupport_param(void)
|
||||
{
|
||||
/* i find it unlikely for a client to use WATCH and MONITOR at the same time, so keep a single limit for both */
|
||||
return STR(MAXWATCH);
|
||||
}
|
||||
|
||||
int monitor_nickchange(Client *client, MessageTag *mtags, char *newnick)
|
||||
{
|
||||
if(!smycmp(client->name, newnick)) // new nick is same as old one, maybe the case changed
|
||||
return 0;
|
||||
|
||||
watch_check(client, RPL_MONOFFLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int monitor_post_nickchange(Client *client, MessageTag *mtags)
|
||||
{
|
||||
watch_check(client, RPL_MONONLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int monitor_quit(Client *client, MessageTag *mtags, char *comment)
|
||||
{
|
||||
watch_check(client, RPL_MONOFFLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int monitor_connect(Client *client)
|
||||
{
|
||||
watch_check(client, RPL_MONONLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int monitor_notification(Client *client, Watch *watch, Link *lp, int reply)
|
||||
{
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR))
|
||||
return 0;
|
||||
|
||||
switch (reply)
|
||||
{
|
||||
case RPL_MONONLINE:
|
||||
sendnumeric(lp->value.client, RPL_MONONLINE, client->name, client->user->username, GetHost(client));
|
||||
break;
|
||||
case RPL_MONOFFLINE:
|
||||
sendnumeric(lp->value.client, RPL_MONOFFLINE, client->name);
|
||||
break;
|
||||
default:
|
||||
break; /* may be handled by other modules */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_status(Client *client, MessageTag *recv_mtags, char *nick)
|
||||
{
|
||||
MessageTag *mtags = NULL;
|
||||
Client *user;
|
||||
user = find_person(nick, NULL);
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
if(!user){
|
||||
sendnumeric(client, RPL_MONOFFLINE, nick);
|
||||
} else {
|
||||
sendnumeric(client, RPL_MONONLINE, user->name, user->user->username, GetHost(user));
|
||||
}
|
||||
free_message_tags(mtags);
|
||||
}
|
||||
|
||||
#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
|
||||
#define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
|
||||
|
||||
CMD_FUNC(cmd_monitor)
|
||||
{
|
||||
char cmd;
|
||||
char *s, *p = NULL;
|
||||
int i;
|
||||
int toomany = 0;
|
||||
Link *lp;
|
||||
|
||||
if(parc < 2 || BadPtr(parv[1]))
|
||||
cmd = 'l';
|
||||
else
|
||||
cmd = tolower(*parv[1]);
|
||||
|
||||
ModDataInfo *watchCounterMD = findmoddata_byname("watchCount", MODDATATYPE_LOCAL_CLIENT);
|
||||
ModDataInfo *watchListMD = findmoddata_byname("watchList", MODDATATYPE_LOCAL_CLIENT);
|
||||
|
||||
if (!watchCounterMD || !watchListMD)
|
||||
{
|
||||
ircd_log(LOG_WARNING, "monitor: moddata not available. Check `watch-backend` module.");
|
||||
sendnotice(client, "MONITOR command is not available at this moment. Please try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case 'c':
|
||||
watch_del_list(client, WATCH_FLAG_TYPE_MONITOR);
|
||||
break;
|
||||
case 'l':
|
||||
lp = WATCH(client);
|
||||
while (lp)
|
||||
{
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR))
|
||||
{
|
||||
lp = lp->next;
|
||||
continue; /* this one is not ours */
|
||||
}
|
||||
sendnumeric(client, RPL_MONLIST, lp->value.wptr->nick);
|
||||
lp = lp->next;
|
||||
}
|
||||
|
||||
sendnumeric(client, RPL_ENDOFMONLIST);
|
||||
break;
|
||||
case 's':
|
||||
lp = WATCH(client);
|
||||
while (lp)
|
||||
{
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR))
|
||||
{
|
||||
lp = lp->next;
|
||||
continue; /* this one is not ours */
|
||||
}
|
||||
send_status(client, recv_mtags, lp->value.wptr->nick);
|
||||
lp = lp->next;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
case '+':
|
||||
if(parc < 3 || BadPtr(parv[2]))
|
||||
return;
|
||||
for(s = strtoken(&p, parv[2], ","); s; s = strtoken(&p, NULL, ",")){
|
||||
if(cmd == '-'){
|
||||
watch_del(s, client, WATCH_FLAG_TYPE_MONITOR);
|
||||
} else {
|
||||
if (WATCHES(client) >= MAXWATCH)
|
||||
{
|
||||
sendnumeric(client, ERR_MONLISTFULL, MAXWATCH, s);
|
||||
continue;
|
||||
}
|
||||
if (do_nick_name(s))
|
||||
watch_add(s, client, WATCH_FLAG_TYPE_MONITOR);
|
||||
send_status(client, recv_mtags, s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-10
@@ -227,12 +227,11 @@ CMD_FUNC(cmd_nick_remote)
|
||||
|
||||
/* Finally set new nick name. */
|
||||
del_from_client_hash_table(client->name, client);
|
||||
hash_check_watch(client, RPL_LOGOFF);
|
||||
|
||||
strcpy(client->name, nick);
|
||||
add_to_client_hash_table(nick, client);
|
||||
|
||||
hash_check_watch(client, RPL_LOGON);
|
||||
RunHook2(HOOKTYPE_POST_REMOTE_NICKCHANGE, client, mtags);
|
||||
}
|
||||
|
||||
CMD_FUNC(cmd_nick_local)
|
||||
@@ -243,7 +242,8 @@ CMD_FUNC(cmd_nick_local)
|
||||
char nick[NICKLEN + 2], descbuf[BUFSIZE];
|
||||
Membership *mp;
|
||||
long lastnick = 0l;
|
||||
int differ = 1, update_watch = 1;
|
||||
int differ = 1;
|
||||
int newuser = 0;
|
||||
unsigned char removemoder = (client->umodes & UMODE_REGNICK) ? 1 : 0;
|
||||
Hook *h;
|
||||
int i = 0;
|
||||
@@ -353,6 +353,8 @@ CMD_FUNC(cmd_nick_local)
|
||||
/* New local client? */
|
||||
if (!client->name[0])
|
||||
{
|
||||
newuser = 1;
|
||||
|
||||
if (iConf.ping_cookie)
|
||||
{
|
||||
/*
|
||||
@@ -389,7 +391,6 @@ CMD_FUNC(cmd_nick_local)
|
||||
*/
|
||||
} else {
|
||||
/* New user! */
|
||||
update_watch = 0; /* already done in register_user() */
|
||||
strlcpy(nick, client->name, sizeof(nick)); /* don't ask, but I need this. do not remove! -- Syzop */
|
||||
}
|
||||
}
|
||||
@@ -456,8 +457,6 @@ CMD_FUNC(cmd_nick_local)
|
||||
}
|
||||
|
||||
del_from_client_hash_table(client->name, client);
|
||||
if (update_watch && IsUser(client))
|
||||
hash_check_watch(client, RPL_LOGOFF);
|
||||
|
||||
strlcpy(client->name, nick, sizeof(client->name));
|
||||
add_to_client_hash_table(nick, client);
|
||||
@@ -466,11 +465,11 @@ CMD_FUNC(cmd_nick_local)
|
||||
snprintf(descbuf, sizeof(descbuf), "Client: %s", nick);
|
||||
fd_desc(client->local->fd, descbuf);
|
||||
|
||||
if (update_watch && IsUser(client))
|
||||
hash_check_watch(client, RPL_LOGON);
|
||||
|
||||
if (removemoder && MyUser(client))
|
||||
sendto_one(client, NULL, ":%s MODE %s :-r", me.name, client->name);
|
||||
|
||||
if (MyUser(client) && !newuser)
|
||||
RunHook2(HOOKTYPE_POST_LOCAL_NICKCHANGE, client, recv_mtags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1099,7 +1098,6 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
|
||||
safe_strdup(client->user->virthost, virthost);
|
||||
}
|
||||
|
||||
hash_check_watch(client, RPL_LOGON); /* Uglier hack */
|
||||
build_umode_string(client, 0, SEND_UMODES|UMODE_SERVNOTICE, buf);
|
||||
|
||||
sendto_serv_butone_nickcmd(client->direction, client, (*buf == '\0' ? "+" : buf));
|
||||
|
||||
@@ -103,7 +103,6 @@ CMD_FUNC(cmd_svsnick)
|
||||
|
||||
add_history(acptr, 1);
|
||||
del_from_client_hash_table(acptr->name, acptr);
|
||||
hash_check_watch(acptr, RPL_LOGOFF);
|
||||
|
||||
sendto_snomask(SNO_NICKCHANGE,
|
||||
"*** %s (%s@%s) has been forced to change their nickname to %s",
|
||||
@@ -111,5 +110,5 @@ CMD_FUNC(cmd_svsnick)
|
||||
|
||||
strlcpy(acptr->name, parv[2], sizeof acptr->name);
|
||||
add_to_client_hash_table(parv[2], acptr);
|
||||
hash_check_watch(acptr, RPL_LOGON);
|
||||
RunHook2(HOOKTYPE_POST_LOCAL_NICKCHANGE, acptr, mtags);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* IRC - Internet Relay Chat, src/modules/watch-backend.c
|
||||
* (C) 2021 The UnrealIRCd Team
|
||||
*
|
||||
* See file AUTHORS in IRC package for additional names of
|
||||
* the programmers.
|
||||
*
|
||||
* 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 1, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
#define WATCH_HASH_TABLE_SIZE 32768
|
||||
|
||||
#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
|
||||
#define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
|
||||
|
||||
ModDataInfo *watchCounterMD;
|
||||
ModDataInfo *watchListMD;
|
||||
static Watch *watchTable[WATCH_HASH_TABLE_SIZE];
|
||||
static int watch_initialized = 0;
|
||||
static char siphashkey_watch[SIPHASH_KEY_LENGTH];
|
||||
|
||||
void dummy_free(ModData *md);
|
||||
void watch_free(ModData *md);
|
||||
|
||||
int watch_backend_user_quit(Client *client, MessageTag *mtags, char *comment);
|
||||
int add_to_watch_hash_table(char *nick, Client *client, int flags);
|
||||
int hash_check_watch(Client *client, int reply);
|
||||
Watch *hash_get_watch(char *nick);
|
||||
int del_from_watch_hash_table(char *nick, Client *client, int flags);
|
||||
int hash_del_watch_list(Client *client, int flags);
|
||||
uint64_t hash_watch_nick_name(const char *name);
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"watch-backend",
|
||||
"5.0",
|
||||
"backend for /watch",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WATCH_ADD, add_to_watch_hash_table);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL, del_from_watch_hash_table);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL_LIST, hash_del_watch_list);
|
||||
EfunctionAddPVoid(modinfo->handle, EFUNC_WATCH_GET, TO_PVOIDFUNC(hash_get_watch));
|
||||
EfunctionAdd(modinfo->handle, EFUNC_WATCH_CHECK, hash_check_watch);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
ModDataInfo mreq;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1); /* or do a complex memory freeing algorithm instead */
|
||||
|
||||
if (!watch_initialized)
|
||||
{
|
||||
memset(watchTable, 0, sizeof(watchTable));
|
||||
siphash_generate_key(siphashkey_watch);
|
||||
watch_initialized = 1;
|
||||
}
|
||||
|
||||
memset(&mreq, 0 , sizeof(mreq));
|
||||
mreq.type = MODDATATYPE_LOCAL_CLIENT;
|
||||
mreq.name = "watchCount",
|
||||
mreq.free = dummy_free;
|
||||
watchCounterMD = ModDataAdd(modinfo->handle, mreq);
|
||||
if (!watchCounterMD)
|
||||
{
|
||||
config_error("[%s] Failed to request user watchCount moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
|
||||
return MOD_FAILED;
|
||||
}
|
||||
|
||||
memset(&mreq, 0 , sizeof(mreq));
|
||||
mreq.type = MODDATATYPE_LOCAL_CLIENT;
|
||||
mreq.name = "watchList",
|
||||
mreq.free = watch_free;
|
||||
watchListMD = ModDataAdd(modinfo->handle, mreq);
|
||||
if (!watchListMD)
|
||||
{
|
||||
config_error("[%s] Failed to request user watchList moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
|
||||
return MOD_FAILED;
|
||||
}
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
void dummy_free(ModData *md)
|
||||
{
|
||||
}
|
||||
|
||||
void watch_free(ModData *md)
|
||||
{
|
||||
/* it should have been never requested to free as the module is PERM */
|
||||
if (md)
|
||||
ircd_log(LOG_WARNING, "MEMORY LEAK: watchList moddata was not freed!");
|
||||
}
|
||||
|
||||
int watch_backend_user_quit(Client *client, MessageTag *mtags, char *comment)
|
||||
{
|
||||
/* Clean out list and watch structures -Donwulff */
|
||||
watch_del_list(client, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_to_watch_hash_table
|
||||
*/
|
||||
int add_to_watch_hash_table(char *nick, Client *client, int flags)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *watch;
|
||||
Link *lp;
|
||||
|
||||
|
||||
/* Get the right bucket... */
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
/* Find the right nick (header) in the bucket, or NULL... */
|
||||
if ((watch = (Watch *)watchTable[hashv]))
|
||||
while (watch && mycmp(watch->nick, nick))
|
||||
watch = watch->hnext;
|
||||
|
||||
/* If found NULL (no header for this nick), make one... */
|
||||
if (!watch) {
|
||||
watch = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick));
|
||||
watch->lasttime = timeofday;
|
||||
strcpy(watch->nick, nick);
|
||||
|
||||
watch->watch = NULL;
|
||||
|
||||
watch->hnext = watchTable[hashv];
|
||||
watchTable[hashv] = watch;
|
||||
}
|
||||
/* Is this client already on the watch-list? */
|
||||
if ((lp = watch->watch))
|
||||
while (lp && (lp->value.client != client))
|
||||
lp = lp->next;
|
||||
|
||||
/* No it isn't, so add it in the bucket and client addint it */
|
||||
if (!lp) {
|
||||
lp = watch->watch;
|
||||
watch->watch = make_link();
|
||||
watch->watch->value.client = client;
|
||||
watch->watch->flags = flags;
|
||||
watch->watch->next = lp;
|
||||
|
||||
lp = make_link();
|
||||
lp->next = WATCH(client);
|
||||
lp->value.wptr = watch;
|
||||
lp->flags = flags;
|
||||
WATCH(client) = lp;
|
||||
WATCHES(client)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_check_watch
|
||||
*/
|
||||
int hash_check_watch(Client *client, int reply)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *watch;
|
||||
Link *lp;
|
||||
|
||||
/* Get us the right bucket */
|
||||
hashv = hash_watch_nick_name(client->name);
|
||||
|
||||
/* Find the right header in this bucket */
|
||||
if ((watch = (Watch *)watchTable[hashv]))
|
||||
while (watch && mycmp(watch->nick, client->name))
|
||||
watch = watch->hnext;
|
||||
if (!watch)
|
||||
return 0; /* This nick isn't on watch */
|
||||
|
||||
/* Update the time of last change to item */
|
||||
watch->lasttime = TStime();
|
||||
|
||||
/* Send notifies out to everybody on the list in header */
|
||||
for (lp = watch->watch; lp; lp = lp->next)
|
||||
{
|
||||
RunHook4(HOOKTYPE_WATCH_NOTIFICATION, client, watch, lp, reply);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_get_watch
|
||||
*/
|
||||
Watch *hash_get_watch(char *nick)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *watch;
|
||||
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
if ((watch = (Watch *)watchTable[hashv]))
|
||||
while (watch && mycmp(watch->nick, nick))
|
||||
watch = watch->hnext;
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
/*
|
||||
* del_from_watch_hash_table
|
||||
*/
|
||||
int del_from_watch_hash_table(char *nick, Client *client, int flags)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch **watch, *wprev;
|
||||
Link **lp, *prev;
|
||||
|
||||
/* Get the bucket for this nick... */
|
||||
hashv = hash_watch_nick_name(nick);
|
||||
|
||||
/* Find the right header, maintaining last-link pointer... */
|
||||
watch = (Watch **)&watchTable[hashv];
|
||||
while (*watch && mycmp((*watch)->nick, nick))
|
||||
watch = &(*watch)->hnext;
|
||||
if (!*watch)
|
||||
return 0; /* No such watch */
|
||||
|
||||
/* Find this client from the list of notifies... with last-ptr. */
|
||||
lp = &(*watch)->watch;
|
||||
while (*lp)
|
||||
{
|
||||
if ((*lp)->value.client == client && ((*lp)->flags & flags) == flags)
|
||||
break;
|
||||
lp = &(*lp)->next;
|
||||
}
|
||||
if (!*lp)
|
||||
return 0; /* No such client to watch */
|
||||
|
||||
/* Fix the linked list under header, then remove the watch entry */
|
||||
prev = *lp;
|
||||
*lp = prev->next;
|
||||
free_link(prev);
|
||||
|
||||
/* Do the same regarding the links in client-record... */
|
||||
lp = (Link **)&WATCH(client);
|
||||
while (*lp && ((*lp)->value.wptr != *watch))
|
||||
lp = &(*lp)->next;
|
||||
|
||||
/*
|
||||
* Give error on the odd case... probobly not even neccessary
|
||||
* No error checking in ircd is unneccessary ;) -Cabal95
|
||||
*/
|
||||
if (!*lp)
|
||||
sendto_ops("WATCH debug error: del_from_watch_hash_table "
|
||||
"found a watch entry with no client "
|
||||
"counterpoint processing nick %s on client %p!",
|
||||
nick, client->user);
|
||||
else {
|
||||
prev = *lp;
|
||||
*lp = prev->next;
|
||||
free_link(prev);
|
||||
}
|
||||
/* In case this header is now empty of notices, remove it */
|
||||
if (!(*watch)->watch) {
|
||||
wprev = *watch;
|
||||
*watch = wprev->hnext;
|
||||
safe_free(wprev);
|
||||
}
|
||||
|
||||
/* Update count of notifies on nick */
|
||||
WATCHES(client)--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_del_watch_list
|
||||
*/
|
||||
int hash_del_watch_list(Client *client, int flags)
|
||||
{
|
||||
unsigned int hashv;
|
||||
Watch *watch;
|
||||
Link **np, **lp, *prev;
|
||||
|
||||
np = (Link **)&WATCH(client);
|
||||
|
||||
while (*np) {
|
||||
if (((*np)->flags & flags) != flags)
|
||||
{
|
||||
/* this entry is not fitting requested flags */
|
||||
np = &(*np)->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
WATCHES(client)--;
|
||||
|
||||
/* Find the watch-record from hash-table... */
|
||||
watch = (*np)->value.wptr;
|
||||
lp = &(watch->watch);
|
||||
while (*lp && ((*lp)->value.client != client))
|
||||
lp = &(*lp)->next;
|
||||
|
||||
/* Not found, another "worst case" debug error */
|
||||
if (!*lp)
|
||||
sendto_ops("WATCH Debug error: hash_del_watch_list "
|
||||
"found a WATCH entry with no table "
|
||||
"counterpoint processing client %s!",
|
||||
client->name);
|
||||
else {
|
||||
/* Fix the watch-list and remove entry */
|
||||
Link *prev = *lp;
|
||||
*lp = prev->next;
|
||||
free_link(prev);
|
||||
|
||||
/*
|
||||
* If this leaves a header without notifies,
|
||||
* remove it. Need to find the last-pointer!
|
||||
*/
|
||||
if (!watch->watch) {
|
||||
Watch **np2, *wprev;
|
||||
|
||||
hashv = hash_watch_nick_name(watch->nick);
|
||||
|
||||
np2 = &watchTable[hashv];
|
||||
while (*np2 && *np2 != watch)
|
||||
np2 = &(*np2)->hnext;
|
||||
|
||||
*np2 = watch->hnext;
|
||||
|
||||
safe_free(watch);
|
||||
}
|
||||
}
|
||||
|
||||
prev = *np; /* Save last pointer processed */
|
||||
*np = prev->next; /* Jump to the next pointer */
|
||||
free_link(prev); /* Free the previous */
|
||||
}
|
||||
|
||||
if (!flags)
|
||||
WATCHES(client) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t hash_watch_nick_name(const char *name)
|
||||
{
|
||||
return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
|
||||
}
|
||||
|
||||
+152
-27
@@ -22,9 +22,15 @@
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
CMD_FUNC(cmd_watch);
|
||||
#define MSG_WATCH "WATCH"
|
||||
|
||||
#define MSG_WATCH "WATCH"
|
||||
CMD_FUNC(cmd_watch);
|
||||
int watch_user_quit(Client *client, MessageTag *mtags, char *comment);
|
||||
int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away);
|
||||
int watch_nickchange(Client *client, MessageTag *mtags, char *newnick);
|
||||
int watch_post_nickchange(Client *client, MessageTag *mtags);
|
||||
int watch_user_connect(Client *client);
|
||||
int watch_notification(Client *client, Watch *watch, Link *lp, int reply);
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
@@ -36,9 +42,21 @@ ModuleHeader MOD_HEADER
|
||||
};
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER);
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_user_quit);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, watch_user_quit);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_AWAY, 0, watch_away);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, watch_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, watch_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_POST_LOCAL_NICKCHANGE, 0, watch_post_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_POST_REMOTE_NICKCHANGE, 0, watch_post_nickchange);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, watch_user_connect);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, watch_user_connect);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_WATCH_NOTIFICATION, 0, watch_notification);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -86,6 +104,9 @@ static void show_watch(Client *client, char *name, int rpl1, int rpl2, int awayn
|
||||
|
||||
static char buf[BUFSIZE];
|
||||
|
||||
#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
|
||||
#define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
|
||||
|
||||
/*
|
||||
* cmd_watch
|
||||
*/
|
||||
@@ -109,6 +130,17 @@ CMD_FUNC(cmd_watch)
|
||||
parv[1] = def;
|
||||
}
|
||||
|
||||
|
||||
ModDataInfo *watchCounterMD = findmoddata_byname("watchCount", MODDATATYPE_LOCAL_CLIENT);
|
||||
ModDataInfo *watchListMD = findmoddata_byname("watchList", MODDATATYPE_LOCAL_CLIENT);
|
||||
|
||||
if (!watchCounterMD || !watchListMD)
|
||||
{
|
||||
ircd_log(LOG_WARNING, "watch: moddata not available. Check `watch-backend` module.");
|
||||
sendnotice(client, "WATCH command is not available at this moment. Please try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " "))
|
||||
{
|
||||
if ((user = strchr(s, '!')))
|
||||
@@ -127,13 +159,15 @@ CMD_FUNC(cmd_watch)
|
||||
continue;
|
||||
if (do_nick_name(s + 1))
|
||||
{
|
||||
if (client->local->watches >= MAXWATCH)
|
||||
if (WATCHES(client) >= MAXWATCH)
|
||||
{
|
||||
sendnumeric(client, ERR_TOOMANYWATCH, s + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
add_to_watch_hash_table(s + 1, client, awaynotify);
|
||||
watch_add(s + 1, client,
|
||||
WATCH_FLAG_TYPE_WATCH | (awaynotify ? WATCH_FLAG_AWAYNOTIFY : 0)
|
||||
);
|
||||
}
|
||||
|
||||
show_watch(client, s + 1, RPL_NOWON, RPL_NOWOFF, awaynotify);
|
||||
@@ -148,7 +182,7 @@ CMD_FUNC(cmd_watch)
|
||||
{
|
||||
if (!*(s+1))
|
||||
continue;
|
||||
del_from_watch_hash_table(s + 1, client);
|
||||
watch_del(s + 1, client, WATCH_FLAG_TYPE_WATCH);
|
||||
show_watch(client, s + 1, RPL_WATCHOFF, RPL_WATCHOFF, 0);
|
||||
|
||||
continue;
|
||||
@@ -160,8 +194,7 @@ CMD_FUNC(cmd_watch)
|
||||
*/
|
||||
if (*s == 'C' || *s == 'c')
|
||||
{
|
||||
hash_del_watch_list(client);
|
||||
|
||||
watch_del_list(client, WATCH_FLAG_TYPE_WATCH);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -173,38 +206,38 @@ CMD_FUNC(cmd_watch)
|
||||
if ((*s == 'S' || *s == 's') && !did_s)
|
||||
{
|
||||
Link *lp;
|
||||
Watch *anptr;
|
||||
Watch *watch;
|
||||
int count = 0;
|
||||
|
||||
did_s = 1;
|
||||
|
||||
/*
|
||||
* Send a list of how many users they have on their WATCH list
|
||||
* and how many WATCH lists they are on.
|
||||
* and how many WATCH lists they are on. This will also include
|
||||
* other WATCH types if present - we're not checking for
|
||||
* WATCH_FLAG_TYPE_*.
|
||||
*/
|
||||
anptr = hash_get_watch(client->name);
|
||||
if (anptr)
|
||||
for (lp = anptr->watch, count = 1;
|
||||
watch = watch_get(client->name);
|
||||
if (watch)
|
||||
for (lp = watch->watch, count = 1;
|
||||
(lp = lp->next); count++)
|
||||
;
|
||||
sendnumeric(client, RPL_WATCHSTAT, client->local->watches, count);
|
||||
sendnumeric(client, RPL_WATCHSTAT, WATCHES(client), count);
|
||||
|
||||
/*
|
||||
* Send a list of everybody in their WATCH list. Be careful
|
||||
* not to buffer overflow.
|
||||
*/
|
||||
if ((lp = client->local->watch) == NULL)
|
||||
{
|
||||
sendnumeric(client, RPL_ENDOFWATCHLIST, *s);
|
||||
continue;
|
||||
}
|
||||
lp = WATCH(client);
|
||||
*buf = '\0';
|
||||
strlcpy(buf, lp->value.wptr->nick, sizeof buf);
|
||||
count =
|
||||
strlen(client->name) + strlen(me.name) + 10 +
|
||||
strlen(buf);
|
||||
while ((lp = lp->next))
|
||||
count = strlen(client->name) + strlen(me.name) + 10;
|
||||
while (lp)
|
||||
{
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_WATCH))
|
||||
{
|
||||
lp = lp->next;
|
||||
continue; /* this one is not ours */
|
||||
}
|
||||
if (count + strlen(lp->value.wptr->nick) + 1 >
|
||||
BUFSIZE - 2)
|
||||
{
|
||||
@@ -215,8 +248,12 @@ CMD_FUNC(cmd_watch)
|
||||
strcat(buf, " ");
|
||||
strcat(buf, lp->value.wptr->nick);
|
||||
count += (strlen(lp->value.wptr->nick) + 1);
|
||||
|
||||
lp = lp->next;
|
||||
}
|
||||
sendnumeric(client, RPL_WATCHLIST, buf);
|
||||
if (*buf)
|
||||
/* anything to send */
|
||||
sendnumeric(client, RPL_WATCHLIST, buf);
|
||||
|
||||
sendnumeric(client, RPL_ENDOFWATCHLIST, *s);
|
||||
continue;
|
||||
@@ -229,12 +266,17 @@ CMD_FUNC(cmd_watch)
|
||||
*/
|
||||
if ((*s == 'L' || *s == 'l') && !did_l)
|
||||
{
|
||||
Link *lp = client->local->watch;
|
||||
Link *lp = WATCH(client);
|
||||
|
||||
did_l = 1;
|
||||
|
||||
while (lp)
|
||||
{
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_WATCH))
|
||||
{
|
||||
lp = lp->next;
|
||||
continue; /* this one is not ours */
|
||||
}
|
||||
if ((target = find_person(lp->value.wptr->nick, NULL)))
|
||||
{
|
||||
sendnumeric(client, RPL_NOWON, target->name,
|
||||
@@ -264,3 +306,86 @@ CMD_FUNC(cmd_watch)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
int watch_user_quit(Client *client, MessageTag *mtags, char *comment)
|
||||
{
|
||||
if (IsUser(client))
|
||||
watch_check(client, RPL_LOGOFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away)
|
||||
{
|
||||
if (reason)
|
||||
watch_check(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY);
|
||||
else
|
||||
watch_check(client, RPL_NOTAWAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watch_nickchange(Client *client, MessageTag *mtags, char *newnick)
|
||||
{
|
||||
watch_check(client, RPL_LOGOFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watch_post_nickchange(Client *client, MessageTag *mtags)
|
||||
{
|
||||
watch_check(client, RPL_LOGON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watch_user_connect(Client *client)
|
||||
{
|
||||
watch_check(client, RPL_LOGON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watch_notification(Client *client, Watch *watch, Link *lp, int reply)
|
||||
{
|
||||
int awaynotify = 0;
|
||||
|
||||
if (!(lp->flags & WATCH_FLAG_TYPE_WATCH))
|
||||
return 0;
|
||||
|
||||
if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY))
|
||||
awaynotify = 1;
|
||||
|
||||
if (!awaynotify)
|
||||
{
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), watch->lasttime, client->info);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* AWAY or UNAWAY */
|
||||
if (!(lp->flags & WATCH_FLAG_AWAYNOTIFY))
|
||||
return 0; /* skip away/unaway notification for users not interested in them */
|
||||
|
||||
if (reply == RPL_NOTAWAY)
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), client->user->lastaway);
|
||||
else /* RPL_GONEAWAY / RPL_REAWAY */
|
||||
sendnumeric(lp->value.client, reply,
|
||||
client->name,
|
||||
(IsUser(client) ? client->user->username : "<N/A>"),
|
||||
(IsUser(client) ?
|
||||
(IsHidden(client) ? client->user->virthost : client->
|
||||
user->realhost) : "<N/A>"), client->user->lastaway, client->user->away);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -774,11 +774,11 @@ static char *replies[] = {
|
||||
/* 727 */ NULL,
|
||||
/* 728 */ NULL,
|
||||
/* 729 */ NULL,
|
||||
/* 730 */ NULL,
|
||||
/* 731 */ NULL,
|
||||
/* 732 */ NULL,
|
||||
/* 733 */ NULL,
|
||||
/* 734 */ NULL,
|
||||
/* 730 RPL_MONONLINE */ ":%s!%s@%s",
|
||||
/* 731 RPL_MONOFFLINE */ ":%s",
|
||||
/* 732 RPL_MONLIST */ ":%s",
|
||||
/* 733 RPL_ENDOFMONLIST */ ":End of MONITOR list",
|
||||
/* 734 ERR_MONLISTFULL */ "%d %s :Monitor list is full.",
|
||||
/* 735 */ NULL,
|
||||
/* 736 */ NULL,
|
||||
/* 737 */ NULL,
|
||||
|
||||
Reference in New Issue
Block a user