unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
watch-backend.c (9771B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/watch-backend.c 3 * (C) 2021 The UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "unrealircd.h" 24 25 #define WATCH_HASH_TABLE_SIZE 32768 26 27 #define WATCHES(client) (moddata_local_client(client, watchCounterMD).i) 28 #define WATCH(client) (moddata_local_client(client, watchListMD).ptr) 29 30 ModDataInfo *watchCounterMD; 31 ModDataInfo *watchListMD; 32 33 static Watch **watchTable = NULL; 34 static char *siphashkey_watch = NULL; 35 36 void dummy_free(ModData *md); 37 void watch_free(ModData *md); 38 39 int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment); 40 int _watch_add(char *nick, Client *client, int flags); 41 int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)); 42 Watch *_watch_get(char *nick); 43 int _watch_del(char *nick, Client *client, int flags); 44 int _watch_del_list(Client *client, int flags); 45 uint64_t hash_watch_nick_name(const char *name); 46 47 ModuleHeader MOD_HEADER 48 = { 49 "watch-backend", 50 "6.0.3", 51 "backend for /WATCH", 52 "UnrealIRCd Team", 53 "unrealircd-6", 54 }; 55 56 MOD_TEST() 57 { 58 MARK_AS_OFFICIAL_MODULE(modinfo); 59 60 EfunctionAdd(modinfo->handle, EFUNC_WATCH_ADD, _watch_add); 61 EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL, _watch_del); 62 EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL_LIST, _watch_del_list); 63 EfunctionAddPVoid(modinfo->handle, EFUNC_WATCH_GET, TO_PVOIDFUNC(_watch_get)); 64 EfunctionAdd(modinfo->handle, EFUNC_WATCH_CHECK, _watch_check); 65 return MOD_SUCCESS; 66 } 67 68 void watch_generic_free(ModData *m) 69 { 70 safe_free(m->ptr); 71 } 72 73 MOD_INIT() 74 { 75 ModDataInfo mreq; 76 77 MARK_AS_OFFICIAL_MODULE(modinfo); 78 ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1); /* or do a complex memory freeing algorithm instead */ 79 80 LoadPersistentPointer(modinfo, siphashkey_watch, watch_generic_free); 81 if (siphashkey_watch == NULL) 82 { 83 siphashkey_watch = safe_alloc(SIPHASH_KEY_LENGTH); 84 siphash_generate_key(siphashkey_watch); 85 } 86 LoadPersistentPointer(modinfo, watchTable, watch_generic_free); 87 if (watchTable == NULL) 88 watchTable = safe_alloc(sizeof(Watch *) * WATCH_HASH_TABLE_SIZE); 89 90 memset(&mreq, 0 , sizeof(mreq)); 91 mreq.type = MODDATATYPE_LOCAL_CLIENT; 92 mreq.name = "watchCount", 93 mreq.free = dummy_free; 94 watchCounterMD = ModDataAdd(modinfo->handle, mreq); 95 if (!watchCounterMD) 96 { 97 config_error("[%s] Failed to request user watchCount moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); 98 return MOD_FAILED; 99 } 100 101 memset(&mreq, 0 , sizeof(mreq)); 102 mreq.type = MODDATATYPE_LOCAL_CLIENT; 103 mreq.name = "watchList", 104 mreq.free = watch_free; 105 watchListMD = ModDataAdd(modinfo->handle, mreq); 106 if (!watchListMD) 107 { 108 config_error("[%s] Failed to request user watchList moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); 109 return MOD_FAILED; 110 } 111 112 HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit); 113 114 return MOD_SUCCESS; 115 } 116 117 MOD_LOAD() 118 { 119 return MOD_SUCCESS; 120 } 121 122 MOD_UNLOAD() 123 { 124 SavePersistentPointer(modinfo, siphashkey_watch); 125 SavePersistentPointer(modinfo, watchTable); 126 return MOD_SUCCESS; 127 } 128 129 void dummy_free(ModData *md) 130 { 131 } 132 133 void watch_free(ModData *md) 134 { 135 /* it should have been never requested to free as the module is PERM */ 136 if (md) 137 { 138 unreal_log(ULOG_ERROR, "watch-backend", "BUG_WATCH_FREE_MEMORY_LEAK", NULL, 139 "[BUG] watchList moddata was not freed -- memory leak!"); 140 } 141 } 142 143 int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment) 144 { 145 /* Clean out list and watch structures -Donwulff */ 146 watch_del_list(client, 0); 147 return 0; 148 } 149 150 /* 151 * _watch_add 152 */ 153 int _watch_add(char *nick, Client *client, int flags) 154 { 155 unsigned int hashv; 156 Watch *watch; 157 Link *lp; 158 159 160 /* Get the right bucket... */ 161 hashv = hash_watch_nick_name(nick); 162 163 /* Find the right nick (header) in the bucket, or NULL... */ 164 if ((watch = watchTable[hashv])) 165 while (watch && mycmp(watch->nick, nick)) 166 watch = watch->hnext; 167 168 /* If found NULL (no header for this nick), make one... */ 169 if (!watch) { 170 watch = safe_alloc(sizeof(Watch)+strlen(nick)); 171 watch->lasttime = timeofday; 172 strcpy(watch->nick, nick); 173 174 watch->watch = NULL; 175 176 watch->hnext = watchTable[hashv]; 177 watchTable[hashv] = watch; 178 } 179 /* Is this client already on the watch-list? */ 180 if ((lp = watch->watch)) 181 while (lp && (lp->value.client != client)) 182 lp = lp->next; 183 184 /* No it isn't, so add it in the bucket and client addint it */ 185 if (!lp) { 186 lp = watch->watch; 187 watch->watch = make_link(); 188 watch->watch->value.client = client; 189 watch->watch->flags = flags; 190 watch->watch->next = lp; 191 192 lp = make_link(); 193 lp->next = WATCH(client); 194 lp->value.wptr = watch; 195 lp->flags = flags; 196 WATCH(client) = lp; 197 WATCHES(client)++; 198 } 199 200 return 0; 201 } 202 203 /* 204 * _watch_check 205 */ 206 int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)) 207 { 208 unsigned int hashv; 209 Watch *watch; 210 Link *lp; 211 212 /* Get us the right bucket */ 213 hashv = hash_watch_nick_name(client->name); 214 215 /* Find the right header in this bucket */ 216 if ((watch = watchTable[hashv])) 217 while (watch && mycmp(watch->nick, client->name)) 218 watch = watch->hnext; 219 if (!watch) 220 return 0; /* This nick isn't on watch */ 221 222 /* Update the time of last change to item */ 223 watch->lasttime = TStime(); 224 225 /* Send notifies out to everybody on the list in header */ 226 for (lp = watch->watch; lp; lp = lp->next) 227 { 228 watch_notify(client, watch, lp, event); 229 } 230 231 return 0; 232 } 233 234 /* 235 * _watch_get 236 */ 237 Watch *_watch_get(char *nick) 238 { 239 unsigned int hashv; 240 Watch *watch; 241 242 hashv = hash_watch_nick_name(nick); 243 244 if ((watch = watchTable[hashv])) 245 while (watch && mycmp(watch->nick, nick)) 246 watch = watch->hnext; 247 248 return watch; 249 } 250 251 /* 252 * _watch_del 253 */ 254 int _watch_del(char *nick, Client *client, int flags) 255 { 256 unsigned int hashv; 257 Watch **watch, *wprev; 258 Link **lp, *prev; 259 260 /* Get the bucket for this nick... */ 261 hashv = hash_watch_nick_name(nick); 262 263 /* Find the right header, maintaining last-link pointer... */ 264 watch = (Watch **)&watchTable[hashv]; 265 while (*watch && mycmp((*watch)->nick, nick)) 266 watch = &(*watch)->hnext; 267 if (!*watch) 268 return 0; /* No such watch */ 269 270 /* Find this client from the list of notifies... with last-ptr. */ 271 lp = &(*watch)->watch; 272 while (*lp) 273 { 274 if ((*lp)->value.client == client && ((*lp)->flags & flags) == flags) 275 break; 276 lp = &(*lp)->next; 277 } 278 if (!*lp) 279 return 0; /* No such client to watch */ 280 281 /* Fix the linked list under header, then remove the watch entry */ 282 prev = *lp; 283 *lp = prev->next; 284 free_link(prev); 285 286 /* Do the same regarding the links in client-record... */ 287 lp = (Link **)&WATCH(client); 288 while (*lp && ((*lp)->value.wptr != *watch)) 289 lp = &(*lp)->next; 290 291 /* 292 * Give error on the odd case... probobly not even neccessary 293 * No error checking in ircd is unneccessary ;) -Cabal95 294 */ 295 if (!*lp) 296 { 297 unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL", client, 298 "[BUG] watch_del found a watch entry with no client counterpoint, " 299 "while processing nick $nick on client $client.details", 300 log_data_string("nick", nick)); 301 } else { 302 prev = *lp; 303 *lp = prev->next; 304 free_link(prev); 305 } 306 /* In case this header is now empty of notices, remove it */ 307 if (!(*watch)->watch) { 308 wprev = *watch; 309 *watch = wprev->hnext; 310 safe_free(wprev); 311 } 312 313 /* Update count of notifies on nick */ 314 WATCHES(client)--; 315 316 return 0; 317 } 318 319 /* 320 * _watch_del_list 321 */ 322 int _watch_del_list(Client *client, int flags) 323 { 324 unsigned int hashv; 325 Watch *watch; 326 Link **np, **lp, *prev; 327 328 np = (Link **)&WATCH(client); 329 330 while (*np) { 331 if (((*np)->flags & flags) != flags) 332 { 333 /* this entry is not fitting requested flags */ 334 np = &(*np)->next; 335 continue; 336 } 337 338 WATCHES(client)--; 339 340 /* Find the watch-record from hash-table... */ 341 watch = (*np)->value.wptr; 342 lp = &(watch->watch); 343 while (*lp && ((*lp)->value.client != client)) 344 lp = &(*lp)->next; 345 346 /* Not found, another "worst case" debug error */ 347 if (!*lp) 348 { 349 unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL_LIST", client, 350 "[BUG] watch_del_list found a watch entry with no table counterpoint, " 351 "while processing client $client.details"); 352 } else { 353 /* Fix the watch-list and remove entry */ 354 Link *prev = *lp; 355 *lp = prev->next; 356 free_link(prev); 357 358 /* 359 * If this leaves a header without notifies, 360 * remove it. Need to find the last-pointer! 361 */ 362 if (!watch->watch) { 363 Watch **np2, *wprev; 364 365 hashv = hash_watch_nick_name(watch->nick); 366 367 np2 = &watchTable[hashv]; 368 while (*np2 && *np2 != watch) 369 np2 = &(*np2)->hnext; 370 371 *np2 = watch->hnext; 372 373 safe_free(watch); 374 } 375 } 376 377 prev = *np; /* Save last pointer processed */ 378 *np = prev->next; /* Jump to the next pointer */ 379 free_link(prev); /* Free the previous */ 380 } 381 382 if (!flags) 383 WATCHES(client) = 0; 384 385 return 0; 386 } 387 388 uint64_t hash_watch_nick_name(const char *name) 389 { 390 return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; 391 } 392