1
0
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:
Bram Matthys
2021-08-06 15:27:46 +02:00
17 changed files with 868 additions and 373 deletions
+8 -1
View File
@@ -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:
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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,
};
+6
View File
@@ -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
View File
@@ -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 {
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+223
View File
@@ -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
View File
@@ -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));
+1 -2
View File
@@ -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);
}
+378
View File
@@ -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
View File
@@ -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
View File
@@ -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,