From bcbcc10b9d2842231734f05cf6538d93098c862c Mon Sep 17 00:00:00 2001 From: k4be Date: Mon, 19 Jul 2021 18:16:28 +0200 Subject: [PATCH 1/8] Move watch code from core to module --- include/h.h | 7 - include/modules.h | 25 ++- include/struct.h | 2 - src/hash.c | 306 ----------------------------- src/misc.c | 4 - src/modules/away.c | 7 +- src/modules/nick.c | 18 +- src/modules/svsnick.c | 3 +- src/modules/watch.c | 437 +++++++++++++++++++++++++++++++++++++++++- 9 files changed, 463 insertions(+), 346 deletions(-) diff --git a/include/h.h b/include/h.h index 69d88a1e8..503a43701 100644 --- a/include/h.h +++ b/include/h.h @@ -334,7 +334,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 #define hash_find_channel find_channel @@ -350,12 +349,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 *); diff --git a/include/modules.h b/include/modules.h index c4c1caa14..4c7d4ab4e 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1165,6 +1165,10 @@ 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 /* Adding a new hook here? * 1) Add the #define HOOKTYPE_.... with a new number * 2) Add a hook prototype (see below) @@ -1523,9 +1527,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 @@ -2115,6 +2120,20 @@ 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); + /** @} */ #ifdef GCC_TYPECHECKING @@ -2225,7 +2244,9 @@ _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)) ) \ _hook_error_incompatible(); #endif /* GCC_TYPECHECKING */ diff --git a/include/struct.h b/include/struct.h index 74786e97c..db5f8d023 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1323,8 +1323,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) */ diff --git a/src/hash.c b/src/hash.c index e5dd20348..94e86e61f 100644 --- a/src/hash.c +++ b/src/hash.c @@ -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; @@ -599,303 +590,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 : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), 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 : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway); - else /* RPL_GONEAWAY / RPL_REAWAY */ - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), 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 */ diff --git a/src/misc.c b/src/misc.c index e629d0173..ba9fcc7c6 100644 --- a/src/misc.c +++ b/src/misc.c @@ -544,8 +544,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); @@ -630,8 +628,6 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char { RunHook3(HOOKTYPE_LOCAL_QUIT, client, recv_mtags, comment); sendto_connectnotice(client, 1, comment); - /* Clean out list and watch structures -Donwulff */ - hash_del_watch_list(client); on_for = TStime() - client->local->firsttime; if (IsHidden(client)) ircd_log(LOG_CLIENT, "Disconnect - (%lld:%lld:%lld) %s!%s@%s [%s] [vhost: %s] (%s)", diff --git a/src/modules/away.c b/src/modules/away.c index a760c83c1..e8ee3240e 100644 --- a/src/modules/away.c +++ b/src/modules/away.c @@ -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); diff --git a/src/modules/nick.c b/src/modules/nick.c index 829393728..f3e90be9c 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -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); } /* @@ -1106,7 +1105,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)); diff --git a/src/modules/svsnick.c b/src/modules/svsnick.c index 3a43e89a4..f2776c4b2 100644 --- a/src/modules/svsnick.c +++ b/src/modules/svsnick.c @@ -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); } diff --git a/src/modules/watch.c b/src/modules/watch.c index f84b607cb..9f58f0c91 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -22,9 +22,34 @@ #include "unrealircd.h" -CMD_FUNC(cmd_watch); +#define MSG_WATCH "WATCH" +#define WATCH_HASH_TABLE_SIZE 32768 -#define MSG_WATCH "WATCH" +#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]; + +CMD_FUNC(cmd_watch); +void dummy_free(ModData *md); +void watch_free(ModData *md); +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 add_to_watch_hash_table(char *nick, Client *client, int awaynotify); +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 hash_del_watch_list(Client *client); +uint64_t hash_watch_nick_name(const char *name); +void count_watch_memory(int *count, u_long *memory); ModuleHeader MOD_HEADER = { @@ -36,9 +61,51 @@ ModuleHeader MOD_HEADER }; MOD_INIT() -{ - CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER); +{ + ModDataInfo mreq; + MARK_AS_OFFICIAL_MODULE(modinfo); + + if (!watch_initialized) + { + memset(watchTable, 0, sizeof(watchTable)); + siphash_generate_key(siphashkey_watch); + watch_initialized = 1; + } + + 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); + + 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; + } + return MOD_SUCCESS; } @@ -52,6 +119,14 @@ MOD_UNLOAD() return MOD_SUCCESS; } +void dummy_free(ModData *md) +{ +} + +void watch_free(ModData *md) +{ +} + /* * RPL_NOWON - Online at the moment (Successfully added to WATCH-list) * RPL_NOWOFF - Offline at the moement (Successfully added to WATCH-list) @@ -127,7 +202,7 @@ 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; @@ -187,13 +262,13 @@ CMD_FUNC(cmd_watch) for (lp = anptr->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) + if ((lp = WATCH(client)) == NULL) { sendnumeric(client, RPL_ENDOFWATCHLIST, *s); continue; @@ -229,7 +304,7 @@ CMD_FUNC(cmd_watch) */ if ((*s == 'L' || *s == 'l') && !did_l) { - Link *lp = client->local->watch; + Link *lp = WATCH(client); did_l = 1; @@ -264,3 +339,349 @@ CMD_FUNC(cmd_watch) */ } } + +int watch_user_quit(Client *client, MessageTag *mtags, char *comment) +{ + if (IsUser(client)) + hash_check_watch(client, RPL_LOGOFF); + + if (MyConnect(client)) + /* Clean out list and watch structures -Donwulff */ + hash_del_watch_list(client); + + return 0; +} + +int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away) +{ + if (reason) + hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY); + else + hash_check_watch(client, RPL_NOTAWAY); + + return 0; +} + +int watch_nickchange(Client *client, MessageTag *mtags, char *newnick) +{ + hash_check_watch(client, RPL_LOGOFF); + + return 0; +} + +int watch_post_nickchange(Client *client, MessageTag *mtags) +{ + hash_check_watch(client, RPL_LOGON); + + return 0; +} + +int watch_user_connect(Client *client) +{ + hash_check_watch(client, RPL_LOGON); + + return 0; +} + +/* + * 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 = WATCH(client); + lp->value.wptr = anptr; + lp->flags = awaynotify; + WATCH(client) = lp; + WATCHES(client)++; + } + + 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), client->user->lastaway); + else /* RPL_GONEAWAY / RPL_REAWAY */ + sendnumeric(lp->value.client, reply, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), 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 = WATCH(client))) + 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 */ + WATCH(client) = 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 */ + WATCHES(client)--; + + return 0; +} + +/* + * hash_del_watch_list + */ +int hash_del_watch_list(Client *client) +{ + unsigned int hashv; + Watch *anptr; + Link *np, *lp, *last; + + + if (!(np = WATCH(client))) + return 0; /* Nothing to do */ + + WATCH(client) = 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 */ + } + + WATCHES(client) = 0; + + return 0; +} + +uint64_t hash_watch_nick_name(const char *name) +{ + return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; +} + +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; + } + } +} + From 6ec6c37839e50f5f0b54fc43130f33990a587a5d Mon Sep 17 00:00:00 2001 From: k4be Date: Mon, 19 Jul 2021 21:28:29 +0200 Subject: [PATCH 2/8] Add watch calls to efunctions. (Doing nothing useful at this point.) Also bump MAXEFUNCTIONS. --- include/h.h | 5 +++++ include/modules.h | 7 ++++++- src/api-efunctions.c | 17 +++++++++++++++++ src/modules/watch.c | 36 +++++++++++++++--------------------- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/include/h.h b/include/h.h index 503a43701..f6abbdacc 100644 --- a/include/h.h +++ b/include/h.h @@ -769,6 +769,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 awaynotify); +extern MODVAR int (*watch_del)(char *nick, Client *client); +extern MODVAR int (*watch_del_list)(Client *client); +extern MODVAR Watch *(*watch_get)(char *nick); +extern MODVAR int (*watch_check)(Client *client, int reply); /* /Efuncs */ /* SSL/TLS functions */ diff --git a/include/modules.h b/include/modules.h index 4c7d4ab4e..4bd975939 100644 --- a/include/modules.h +++ b/include/modules.h @@ -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) @@ -2356,6 +2356,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, }; /* Module flags */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 4412af13d..20a8dc868 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -121,6 +121,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 awaynotify); +int (*watch_del)(char *nick, Client *client); +int (*watch_del_list)(Client *client); +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)()) { @@ -277,6 +282,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; @@ -365,4 +376,10 @@ 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); } + diff --git a/src/modules/watch.c b/src/modules/watch.c index 9f58f0c91..4909c0f49 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -45,11 +45,10 @@ int watch_user_connect(Client *client); int add_to_watch_hash_table(char *nick, Client *client, int awaynotify); int hash_check_watch(Client *client, int reply); -Watch *hash_get_watch(char *nick); +Watch *hash_get_watch(char *nick); int del_from_watch_hash_table(char *nick, Client *client); -int hash_del_watch_list(Client *client); +int hash_del_watch_list(Client *client); uint64_t hash_watch_nick_name(const char *name); -void count_watch_memory(int *count, u_long *memory); ModuleHeader MOD_HEADER = { @@ -60,6 +59,18 @@ ModuleHeader MOD_HEADER "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; @@ -503,7 +514,7 @@ int hash_check_watch(Client *client, int reply) /* * hash_get_watch */ -Watch *hash_get_watch(char *nick) +Watch *hash_get_watch(char *nick) { unsigned int hashv; Watch *anptr; @@ -668,20 +679,3 @@ uint64_t hash_watch_nick_name(const char *name) return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; } -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; - } - } -} - From 07006403300444de43ed597c12f1fa6a8e5b81d3 Mon Sep 17 00:00:00 2001 From: k4be Date: Tue, 20 Jul 2021 20:26:31 +0200 Subject: [PATCH 3/8] Create "watch-backend" module that will serve for all watch-like commands. --- Makefile.windows | 6 +- doc/conf/modules.default.conf | 1 + src/modules/Makefile.in | 6 +- src/modules/watch-backend.c | 408 ++++++++++++++++++++++++++++++++++ src/modules/watch.c | 389 +++----------------------------- 5 files changed, 444 insertions(+), 366 deletions(-) create mode 100644 src/modules/watch-backend.c diff --git a/Makefile.windows b/Makefile.windows index f63f1ec92..69db5a7a0 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -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,8 @@ 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) dummy: diff --git a/doc/conf/modules.default.conf b/doc/conf/modules.default.conf index 28dd7f972..7b8c4fb06 100644 --- a/doc/conf/modules.default.conf +++ b/doc/conf/modules.default.conf @@ -235,3 +235,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 */ diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index cdc560f04..11a5208ee 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -74,7 +74,7 @@ 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 MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -652,6 +652,10 @@ targetfloodprot.so: targetfloodprot.c $(INCLUDES) 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 ############################################################################# # capabilities diff --git a/src/modules/watch-backend.c b/src/modules/watch-backend.c new file mode 100644 index 000000000..a32303efe --- /dev/null +++ b/src/modules/watch-backend.c @@ -0,0 +1,408 @@ +/* + * 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 add_to_watch_hash_table(char *nick, Client *client, int awaynotify); +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 hash_del_watch_list(Client *client); +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); + + 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; + } + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +void dummy_free(ModData *md) +{ +} + +void watch_free(ModData *md) +{ +#warning do proper free +} + +/* + * 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 = WATCH(client); + lp->value.wptr = anptr; + lp->flags = awaynotify; + WATCH(client) = lp; + WATCHES(client)++; + } + + 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), client->user->lastaway); + else /* RPL_GONEAWAY / RPL_REAWAY */ + sendnumeric(lp->value.client, reply, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), 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 = WATCH(client))) + 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 */ + WATCH(client) = 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 */ + WATCHES(client)--; + + return 0; +} + +/* + * hash_del_watch_list + */ +int hash_del_watch_list(Client *client) +{ + unsigned int hashv; + Watch *anptr; + Link *np, *lp, *last; + + + if (!(np = WATCH(client))) + return 0; /* Nothing to do */ + + WATCH(client) = 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 */ + } + + WATCHES(client) = 0; + + return 0; +} + +uint64_t hash_watch_nick_name(const char *name) +{ + return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; +} + diff --git a/src/modules/watch.c b/src/modules/watch.c index 4909c0f49..6e12c6c5c 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -23,16 +23,6 @@ #include "unrealircd.h" #define MSG_WATCH "WATCH" -#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]; CMD_FUNC(cmd_watch); void dummy_free(ModData *md); @@ -43,13 +33,6 @@ int watch_nickchange(Client *client, MessageTag *mtags, char *newnick); int watch_post_nickchange(Client *client, MessageTag *mtags); int watch_user_connect(Client *client); -int add_to_watch_hash_table(char *nick, Client *client, int awaynotify); -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 hash_del_watch_list(Client *client); -uint64_t hash_watch_nick_name(const char *name); - ModuleHeader MOD_HEADER = { "watch", @@ -59,31 +42,10 @@ ModuleHeader MOD_HEADER "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); - if (!watch_initialized) - { - memset(watchTable, 0, sizeof(watchTable)); - siphash_generate_key(siphashkey_watch); - watch_initialized = 1; - } - 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); @@ -95,28 +57,6 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, watch_user_connect); HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, watch_user_connect); - 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; - } - return MOD_SUCCESS; } @@ -130,14 +70,6 @@ MOD_UNLOAD() return MOD_SUCCESS; } -void dummy_free(ModData *md) -{ -} - -void watch_free(ModData *md) -{ -} - /* * RPL_NOWON - Online at the moment (Successfully added to WATCH-list) * RPL_NOWOFF - Offline at the moement (Successfully added to WATCH-list) @@ -172,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 */ @@ -195,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, '!'))) @@ -219,7 +165,7 @@ CMD_FUNC(cmd_watch) continue; } - add_to_watch_hash_table(s + 1, client, awaynotify); + watch_add(s + 1, client, awaynotify); } show_watch(client, s + 1, RPL_NOWON, RPL_NOWOFF, awaynotify); @@ -234,7 +180,7 @@ CMD_FUNC(cmd_watch) { if (!*(s+1)) continue; - del_from_watch_hash_table(s + 1, client); + watch_del(s + 1, client); show_watch(client, s + 1, RPL_WATCHOFF, RPL_WATCHOFF, 0); continue; @@ -246,7 +192,7 @@ CMD_FUNC(cmd_watch) */ if (*s == 'C' || *s == 'c') { - hash_del_watch_list(client); + watch_del_list(client); continue; } @@ -268,7 +214,7 @@ CMD_FUNC(cmd_watch) * Send a list of how many users they have on their WATCH list * and how many WATCH lists they are on. */ - anptr = hash_get_watch(client->name); + anptr = watch_get(client->name); if (anptr) for (lp = anptr->watch, count = 1; (lp = lp->next); count++) @@ -354,11 +300,11 @@ CMD_FUNC(cmd_watch) int watch_user_quit(Client *client, MessageTag *mtags, char *comment) { if (IsUser(client)) - hash_check_watch(client, RPL_LOGOFF); + watch_check(client, RPL_LOGOFF); if (MyConnect(client)) /* Clean out list and watch structures -Donwulff */ - hash_del_watch_list(client); + watch_del_list(client); return 0; } @@ -366,316 +312,31 @@ int watch_user_quit(Client *client, MessageTag *mtags, char *comment) int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away) { if (reason) - hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY); + watch_check(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY); else - hash_check_watch(client, RPL_NOTAWAY); + watch_check(client, RPL_NOTAWAY); return 0; } int watch_nickchange(Client *client, MessageTag *mtags, char *newnick) { - hash_check_watch(client, RPL_LOGOFF); + watch_check(client, RPL_LOGOFF); return 0; } int watch_post_nickchange(Client *client, MessageTag *mtags) { - hash_check_watch(client, RPL_LOGON); + watch_check(client, RPL_LOGON); return 0; } int watch_user_connect(Client *client) { - hash_check_watch(client, RPL_LOGON); + watch_check(client, RPL_LOGON); return 0; } -/* - * 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 = WATCH(client); - lp->value.wptr = anptr; - lp->flags = awaynotify; - WATCH(client) = lp; - WATCHES(client)++; - } - - 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 : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), 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 : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway); - else /* RPL_GONEAWAY / RPL_REAWAY */ - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), 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 = WATCH(client))) - 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 */ - WATCH(client) = 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 */ - WATCHES(client)--; - - return 0; -} - -/* - * hash_del_watch_list - */ -int hash_del_watch_list(Client *client) -{ - unsigned int hashv; - Watch *anptr; - Link *np, *lp, *last; - - - if (!(np = WATCH(client))) - return 0; /* Nothing to do */ - - WATCH(client) = 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 */ - } - - WATCHES(client) = 0; - - return 0; -} - -uint64_t hash_watch_nick_name(const char *name) -{ - return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; -} - From 7fab97d364cf96ef556dce1421178ba6aed679b8 Mon Sep 17 00:00:00 2001 From: k4be Date: Tue, 20 Jul 2021 20:59:36 +0200 Subject: [PATCH 4/8] Add watch notification hook. This will likely reduce performance, but this should not matter in modern times. Also added flags to let modules know which one the entry belongs to, and what to do with it. Now modules should be able to add their own WATCH methods (like IRCv3 MONITOR), or extend functionality to notify about other changes than the default log on, log off and away statuses (like SETNAMEs). --- include/h.h | 2 +- include/modules.h | 14 +++- include/struct.h | 8 ++ src/api-efunctions.c | 2 +- src/modules/watch-backend.c | 149 ++++++++++++++---------------------- src/modules/watch.c | 58 ++++++++++++-- 6 files changed, 134 insertions(+), 99 deletions(-) diff --git a/include/h.h b/include/h.h index f6abbdacc..abe58ab52 100644 --- a/include/h.h +++ b/include/h.h @@ -769,7 +769,7 @@ 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 awaynotify); +extern MODVAR int (*watch_add)(char *nick, Client *client, int flags); extern MODVAR int (*watch_del)(char *nick, Client *client); extern MODVAR int (*watch_del_list)(Client *client); extern MODVAR Watch *(*watch_get)(char *nick); diff --git a/include/modules.h b/include/modules.h index 4bd975939..9459c20a1 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1169,6 +1169,8 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #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) @@ -2134,6 +2136,15 @@ int hooktype_post_local_nickchange(Client *client, MessageTag *mtags); */ 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 @@ -2246,7 +2257,8 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, 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_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) || \ + ((hooktype == HOOKTYPE_WATCH_NOTIFICATION) && !ValidateHook(hooktype_watch_notification, func)) )\ _hook_error_incompatible(); #endif /* GCC_TYPECHECKING */ diff --git a/include/struct.h b/include/struct.h index db5f8d023..4560bbc37 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1969,6 +1969,14 @@ 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 */ + +/* behaviour switches */ +#define WATCH_FLAG_AWAYNOTIFY (1<<8) /* should send AWAY notifications */ + /* Used for notify-hash buckets... -Donwulff */ struct Watch { diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 20a8dc868..1781fbebd 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -121,7 +121,7 @@ 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 awaynotify); +int (*watch_add)(char *nick, Client *client, int flags); int (*watch_del)(char *nick, Client *client); int (*watch_del_list)(Client *client); Watch *(*watch_get)(char *nick); diff --git a/src/modules/watch-backend.c b/src/modules/watch-backend.c index a32303efe..f5a908aca 100644 --- a/src/modules/watch-backend.c +++ b/src/modules/watch-backend.c @@ -36,7 +36,7 @@ static char siphashkey_watch[SIPHASH_KEY_LENGTH]; void dummy_free(ModData *md); void watch_free(ModData *md); -int add_to_watch_hash_table(char *nick, Client *client, int awaynotify); +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); @@ -124,10 +124,10 @@ void watch_free(ModData *md) /* * add_to_watch_hash_table */ -int add_to_watch_hash_table(char *nick, Client *client, int awaynotify) +int add_to_watch_hash_table(char *nick, Client *client, int flags) { unsigned int hashv; - Watch *anptr; + Watch *watch; Link *lp; @@ -135,38 +135,38 @@ int add_to_watch_hash_table(char *nick, Client *client, int awaynotify) 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 ((watch = (Watch *)watchTable[hashv])) + while (watch && mycmp(watch->nick, nick)) + watch = watch->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); + if (!watch) { + watch = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick)); + watch->lasttime = timeofday; + strcpy(watch->nick, nick); - anptr->watch = NULL; + watch->watch = NULL; - anptr->hnext = watchTable[hashv]; - watchTable[hashv] = anptr; + watch->hnext = watchTable[hashv]; + watchTable[hashv] = watch; } /* Is this client already on the watch-list? */ - if ((lp = anptr->watch)) + 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 = anptr->watch; - anptr->watch = make_link(); - anptr->watch->value.client = client; - anptr->watch->flags = awaynotify; - anptr->watch->next = 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 = anptr; - lp->flags = awaynotify; + lp->value.wptr = watch; + lp->flags = flags; WATCH(client) = lp; WATCHES(client)++; } @@ -180,59 +180,26 @@ int add_to_watch_hash_table(char *nick, Client *client, int awaynotify) int hash_check_watch(Client *client, int reply) { unsigned int hashv; - Watch *anptr; + Watch *watch; 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) + 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 */ - anptr->lasttime = TStime(); + watch->lasttime = TStime(); /* Send notifies out to everybody on the list in header */ - for (lp = anptr->watch; lp; lp = lp->next) + for (lp = watch->watch; lp; lp = lp->next) { - if (!awaynotify) - { - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), 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 : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway); - else /* RPL_GONEAWAY / RPL_REAWAY */ - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway, client->user->away); - } + RunHook4(HOOKTYPE_WATCH_NOTIFICATION, client, watch, lp, reply); } return 0; @@ -244,15 +211,15 @@ int hash_check_watch(Client *client, int reply) Watch *hash_get_watch(char *nick) { unsigned int hashv; - Watch *anptr; + Watch *watch; hashv = hash_watch_nick_name(nick); - if ((anptr = (Watch *)watchTable[hashv])) - while (anptr && mycmp(anptr->nick, nick)) - anptr = anptr->hnext; + if ((watch = (Watch *)watchTable[hashv])) + while (watch && mycmp(watch->nick, nick)) + watch = watch->hnext; - return anptr; + return watch; } /* @@ -261,23 +228,23 @@ Watch *hash_get_watch(char *nick) int del_from_watch_hash_table(char *nick, Client *client) { unsigned int hashv; - Watch *anptr, *nlast = NULL; + Watch *watch, *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 ((watch = (Watch *)watchTable[hashv])) + while (watch && mycmp(watch->nick, nick)) { + nlast = watch; + watch = watch->hnext; } - if (!anptr) + if (!watch) return 0; /* No such watch */ /* Find this client from the list of notifies... with last-ptr. */ - if ((lp = anptr->watch)) + if ((lp = watch->watch)) while (lp && (lp->value.client != client)) { last = lp; lp = lp->next; @@ -287,7 +254,7 @@ int del_from_watch_hash_table(char *nick, Client *client) /* Fix the linked list under header, then remove the watch entry */ if (!last) - anptr->watch = lp->next; + watch->watch = lp->next; else last->next = lp->next; free_link(lp); @@ -295,7 +262,7 @@ int del_from_watch_hash_table(char *nick, Client *client) /* Do the same regarding the links in client-record... */ last = NULL; if ((lp = WATCH(client))) - while (lp && (lp->value.wptr != anptr)) { + while (lp && (lp->value.wptr != watch)) { last = lp; lp = lp->next; } @@ -317,12 +284,12 @@ int del_from_watch_hash_table(char *nick, Client *client) free_link(lp); } /* In case this header is now empty of notices, remove it */ - if (!anptr->watch) { + if (!watch->watch) { if (!nlast) - watchTable[hashv] = anptr->hnext; + watchTable[hashv] = watch->hnext; else - nlast->hnext = anptr->hnext; - safe_free(anptr); + nlast->hnext = watch->hnext; + safe_free(watch); } /* Update count of notifies on nick */ @@ -334,10 +301,10 @@ int del_from_watch_hash_table(char *nick, Client *client) /* * hash_del_watch_list */ -int hash_del_watch_list(Client *client) +int hash_del_watch_list(Client *client) { unsigned int hashv; - Watch *anptr; + Watch *watch; Link *np, *lp, *last; @@ -347,9 +314,9 @@ int hash_del_watch_list(Client *client) WATCH(client) = NULL; /* Break the watch-list for client */ while (np) { /* Find the watch-record from hash-table... */ - anptr = np->value.wptr; + watch = np->value.wptr; last = NULL; - for (lp = anptr->watch; lp && (lp->value.client != client); + for (lp = watch->watch; lp && (lp->value.client != client); lp = lp->next) last = lp; @@ -362,7 +329,7 @@ int hash_del_watch_list(Client *client) else { /* Fix the watch-list and remove entry */ if (!last) - anptr->watch = lp->next; + watch->watch = lp->next; else last->next = lp->next; free_link(lp); @@ -371,23 +338,23 @@ int hash_del_watch_list(Client *client) * If this leaves a header without notifies, * remove it. Need to find the last-pointer! */ - if (!anptr->watch) { + if (!watch->watch) { Watch *np2, *nl; - hashv = hash_watch_nick_name(anptr->nick); + hashv = hash_watch_nick_name(watch->nick); nl = NULL; np2 = watchTable[hashv]; - while (np2 != anptr) { + while (np2 != watch) { nl = np2; np2 = np2->hnext; } if (nl) - nl->hnext = anptr->hnext; + nl->hnext = watch->hnext; else - watchTable[hashv] = anptr->hnext; - safe_free(anptr); + watchTable[hashv] = watch->hnext; + safe_free(watch); } } diff --git a/src/modules/watch.c b/src/modules/watch.c index 6e12c6c5c..547238cd4 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -32,6 +32,7 @@ int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_a 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 = { @@ -56,6 +57,7 @@ MOD_INIT() 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; } @@ -165,7 +167,9 @@ CMD_FUNC(cmd_watch) continue; } - watch_add(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); @@ -205,7 +209,7 @@ CMD_FUNC(cmd_watch) if ((*s == 'S' || *s == 's') && !did_s) { Link *lp; - Watch *anptr; + Watch *watch; int count = 0; did_s = 1; @@ -214,9 +218,9 @@ CMD_FUNC(cmd_watch) * Send a list of how many users they have on their WATCH list * and how many WATCH lists they are on. */ - anptr = watch_get(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, WATCHES(client), count); @@ -340,3 +344,47 @@ int watch_user_connect(Client *client) 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), 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 : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), client->user->lastaway); + else /* RPL_GONEAWAY / RPL_REAWAY */ + sendnumeric(lp->value.client, reply, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? + (IsHidden(client) ? client->user->virthost : client-> + user->realhost) : ""), client->user->lastaway, client->user->away); + } + + return 0; +} + From b01bc76a69ecad525f05f81fea1b643f34b03da8 Mon Sep 17 00:00:00 2001 From: k4be Date: Tue, 20 Jul 2021 21:38:42 +0200 Subject: [PATCH 5/8] WATCH command should only modify own entries. --- src/modules/watch.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/watch.c b/src/modules/watch.c index 547238cd4..0bdfc0d65 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -197,7 +197,7 @@ CMD_FUNC(cmd_watch) if (*s == 'C' || *s == 'c') { watch_del_list(client); - +#warning remove only my entries continue; } @@ -216,7 +216,9 @@ CMD_FUNC(cmd_watch) /* * 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_*. */ watch = watch_get(client->name); if (watch) @@ -241,6 +243,8 @@ CMD_FUNC(cmd_watch) strlen(buf); while ((lp = lp->next)) { + if (!(lp->flags & WATCH_FLAG_TYPE_WATCH)) + continue; /* this one is not ours */ if (count + strlen(lp->value.wptr->nick) + 1 > BUFSIZE - 2) { @@ -271,6 +275,8 @@ CMD_FUNC(cmd_watch) while (lp) { + if (!(lp->flags & WATCH_FLAG_TYPE_WATCH)) + continue; /* this one is not ours */ if ((target = find_person(lp->value.wptr->nick, NULL))) { sendnumeric(client, RPL_NOWON, target->name, From 4ec455bf0641f471eaa765c27dbbad85b9259fdc Mon Sep 17 00:00:00 2001 From: k4be Date: Wed, 21 Jul 2021 18:04:01 +0200 Subject: [PATCH 6/8] Add selective watch removal. Clean up linked list item deleting. --- include/h.h | 4 +- src/api-efunctions.c | 4 +- src/modules/watch-backend.c | 199 ++++++++++++++++++------------------ src/modules/watch.c | 10 +- 4 files changed, 107 insertions(+), 110 deletions(-) diff --git a/include/h.h b/include/h.h index abe58ab52..6eabb2829 100644 --- a/include/h.h +++ b/include/h.h @@ -770,8 +770,8 @@ 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); -extern MODVAR int (*watch_del_list)(Client *client); +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); /* /Efuncs */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 1781fbebd..b8be57474 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -122,8 +122,8 @@ 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 (*watch_del_list)(Client *client); +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); diff --git a/src/modules/watch-backend.c b/src/modules/watch-backend.c index f5a908aca..5dca14c1a 100644 --- a/src/modules/watch-backend.c +++ b/src/modules/watch-backend.c @@ -36,21 +36,22 @@ 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 hash_del_watch_list(Client *client); +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() { @@ -69,6 +70,7 @@ 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) { @@ -99,6 +101,8 @@ MOD_INIT() return MOD_FAILED; } + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit); + return MOD_SUCCESS; } @@ -118,7 +122,16 @@ void dummy_free(ModData *md) void watch_free(ModData *md) { -#warning do proper free + /* 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; } /* @@ -127,8 +140,8 @@ void watch_free(ModData *md) int add_to_watch_hash_table(char *nick, Client *client, int flags) { unsigned int hashv; - Watch *watch; - Link *lp; + Watch *watch; + Link *lp; /* Get the right bucket... */ @@ -136,7 +149,7 @@ int add_to_watch_hash_table(char *nick, Client *client, int flags) /* Find the right nick (header) in the bucket, or NULL... */ if ((watch = (Watch *)watchTable[hashv])) - while (watch && mycmp(watch->nick, nick)) + while (watch && mycmp(watch->nick, nick)) watch = watch->hnext; /* If found NULL (no header for this nick), make one... */ @@ -152,7 +165,7 @@ int add_to_watch_hash_table(char *nick, Client *client, int flags) } /* Is this client already on the watch-list? */ if ((lp = watch->watch)) - while (lp && (lp->value.client != client)) + while (lp && (lp->value.client != client)) lp = lp->next; /* No it isn't, so add it in the bucket and client addint it */ @@ -175,23 +188,23 @@ int add_to_watch_hash_table(char *nick, Client *client, int flags) } /* - * hash_check_watch + * hash_check_watch */ int hash_check_watch(Client *client, int reply) { unsigned int hashv; - Watch *watch; - Link *lp; + 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)) + while (watch && mycmp(watch->nick, client->name)) watch = watch->hnext; if (!watch) - return 0; /* This nick isn't on watch */ + return 0; /* This nick isn't on watch */ /* Update the time of last change to item */ watch->lasttime = TStime(); @@ -211,12 +224,12 @@ int hash_check_watch(Client *client, int reply) Watch *hash_get_watch(char *nick) { unsigned int hashv; - Watch *watch; + Watch *watch; hashv = hash_watch_nick_name(nick); if ((watch = (Watch *)watchTable[hashv])) - while (watch && mycmp(watch->nick, nick)) + while (watch && mycmp(watch->nick, nick)) watch = watch->hnext; return watch; @@ -225,71 +238,62 @@ Watch *hash_get_watch(char *nick) /* * del_from_watch_hash_table */ -int del_from_watch_hash_table(char *nick, Client *client) +int del_from_watch_hash_table(char *nick, Client *client, int flags) { unsigned int hashv; - Watch *watch, *nlast = NULL; - Link *lp, *last = NULL; + 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... */ - if ((watch = (Watch *)watchTable[hashv])) - while (watch && mycmp(watch->nick, nick)) { - nlast = watch; - watch = watch->hnext; - } - if (!watch) - return 0; /* No such watch */ + 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. */ - if ((lp = watch->watch)) - while (lp && (lp->value.client != client)) { - last = lp; - lp = lp->next; - } - if (!lp) - return 0; /* No such client to watch */ + 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 */ - if (!last) - watch->watch = lp->next; - else - last->next = lp->next; - free_link(lp); + prev = *lp; + *lp = prev->next; + free_link(prev); /* Do the same regarding the links in client-record... */ - last = NULL; - if ((lp = WATCH(client))) - while (lp && (lp->value.wptr != watch)) { - last = lp; - lp = lp->next; - } + 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 " + 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 */ - WATCH(client) = lp->next; - else - last->next = lp->next; - free_link(lp); + prev = *lp; + *lp = prev->next; + free_link(prev); } /* In case this header is now empty of notices, remove it */ - if (!watch->watch) { - if (!nlast) - watchTable[hashv] = watch->hnext; - else - nlast->hnext = watch->hnext; - safe_free(watch); + if (!(*watch)->watch) { + wprev = *watch; + *watch = wprev->hnext; + safe_free(wprev); } /* Update count of notifies on nick */ @@ -301,69 +305,68 @@ int del_from_watch_hash_table(char *nick, Client *client) /* * hash_del_watch_list */ -int hash_del_watch_list(Client *client) +int hash_del_watch_list(Client *client, int flags) { - unsigned int hashv; - Watch *watch; - Link *np, *lp, *last; + unsigned int hashv; + Watch *watch; + Link **np, **lp, *prev; + np = (Link **)&WATCH(client); - if (!(np = WATCH(client))) - return 0; /* Nothing to do */ - - WATCH(client) = NULL; /* Break the watch-list for client */ - while (np) { - /* Find the watch-record from hash-table... */ - watch = np->value.wptr; - last = NULL; - for (lp = watch->watch; lp && (lp->value.client != client); - lp = lp->next) - last = lp; + 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); + 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) - watch->watch = lp->next; - else - last->next = lp->next; - free_link(lp); + 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, *nl; + Watch **np2, *wprev; hashv = hash_watch_nick_name(watch->nick); - nl = NULL; - np2 = watchTable[hashv]; - while (np2 != watch) { - nl = np2; - np2 = np2->hnext; - } - - if (nl) - nl->hnext = watch->hnext; - else - watchTable[hashv] = watch->hnext; + np2 = &watchTable[hashv]; + while (*np2 && *np2 != watch) + np2 = &(*np2)->hnext; + + *np2 = watch->hnext; + safe_free(watch); } } - lp = np; /* Save last pointer processed */ - np = np->next; /* Jump to the next pointer */ - free_link(lp); /* Free the previous */ + prev = *np; /* Save last pointer processed */ + *np = prev->next; /* Jump to the next pointer */ + free_link(prev); /* Free the previous */ } - WATCHES(client) = 0; + if (!flags) + WATCHES(client) = 0; return 0; } diff --git a/src/modules/watch.c b/src/modules/watch.c index 0bdfc0d65..99f487127 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -184,7 +184,7 @@ CMD_FUNC(cmd_watch) { if (!*(s+1)) continue; - watch_del(s + 1, client); + watch_del(s + 1, client, WATCH_FLAG_TYPE_WATCH); show_watch(client, s + 1, RPL_WATCHOFF, RPL_WATCHOFF, 0); continue; @@ -196,8 +196,7 @@ CMD_FUNC(cmd_watch) */ if (*s == 'C' || *s == 'c') { - watch_del_list(client); -#warning remove only my entries + watch_del_list(client, WATCH_FLAG_TYPE_WATCH); continue; } @@ -311,11 +310,6 @@ int watch_user_quit(Client *client, MessageTag *mtags, char *comment) { if (IsUser(client)) watch_check(client, RPL_LOGOFF); - - if (MyConnect(client)) - /* Clean out list and watch structures -Donwulff */ - watch_del_list(client); - return 0; } From b0c30cd1a61af550a7c32e7e3c93b0157ef4102a Mon Sep 17 00:00:00 2001 From: k4be Date: Wed, 21 Jul 2021 18:06:31 +0200 Subject: [PATCH 7/8] Remove unused prototypes --- src/modules/watch.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/watch.c b/src/modules/watch.c index 99f487127..4389eaa51 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -25,8 +25,6 @@ #define MSG_WATCH "WATCH" CMD_FUNC(cmd_watch); -void dummy_free(ModData *md); -void watch_free(ModData *md); 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); From 5c9b7cf15fa1938ac6a2a764788d1785149518de Mon Sep 17 00:00:00 2001 From: k4be Date: Wed, 21 Jul 2021 20:23:07 +0200 Subject: [PATCH 8/8] Add MONITOR command and fix numerous problems with WATCH. --- Makefile.windows | 3 + doc/conf/modules.default.conf | 1 + include/numeric.h | 6 + include/struct.h | 1 + src/modules/Makefile.in | 9 +- src/modules/monitor.c | 223 ++++++++++++++++++++++++++++++++++ src/modules/watch.c | 25 ++-- src/numeric.c | 10 +- 8 files changed, 260 insertions(+), 18 deletions(-) create mode 100644 src/modules/monitor.c diff --git a/Makefile.windows b/Makefile.windows index 69db5a7a0..0ef07a5a7 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -1130,5 +1130,8 @@ src/modules/clienttagdeny.dll: src/modules/clienttagdeny.c $(INCLUDES) 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: diff --git a/doc/conf/modules.default.conf b/doc/conf/modules.default.conf index 7b8c4fb06..adcc60057 100644 --- a/doc/conf/modules.default.conf +++ b/doc/conf/modules.default.conf @@ -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 ***/ diff --git a/include/numeric.h b/include/numeric.h index fce426860..e291a963c 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -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 diff --git a/include/struct.h b/include/struct.h index 4560bbc37..ecfcd2218 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1973,6 +1973,7 @@ struct Mode { /* 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 */ diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index 11a5208ee..628b80f94 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -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 watch-backend.so + targetfloodprot.so clienttagdeny.so watch-backend.so \ + monitor.so MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -652,11 +653,15 @@ targetfloodprot.so: targetfloodprot.c $(INCLUDES) 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 ############################################################################# diff --git a/src/modules/monitor.c b/src/modules/monitor.c new file mode 100644 index 000000000..106e267a6 --- /dev/null +++ b/src/modules/monitor.c @@ -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; + } +} + diff --git a/src/modules/watch.c b/src/modules/watch.c index 4389eaa51..215f8235f 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -228,20 +228,16 @@ CMD_FUNC(cmd_watch) * Send a list of everybody in their WATCH list. Be careful * not to buffer overflow. */ - if ((lp = WATCH(client)) == 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) { @@ -252,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; @@ -273,7 +273,10 @@ CMD_FUNC(cmd_watch) 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, diff --git a/src/numeric.c b/src/numeric.c index 14812401b..474021c83 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -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,