unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
api-clicap.c (9670B)
1 /************************************************************************ 2 * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/api-clicap.c 3 * (c) 2015- Bram Matthys and 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 ADVERTISEONLYCAPS 16 26 /* Advertise only caps are not counted anywhere, this only provides space in rehash temporary storage arrays. 27 * If exceeded, the caps just won't be stored and will be re-added safely. --k4be 28 */ 29 30 #define MAXCLICAPS ((int)(sizeof(long)*8 - 1 + ADVERTISEONLYCAPS)) /* how many cap bits will fit in `long`? */ 31 static char *old_caps[MAXCLICAPS]; /**< List of old CAP names - used for /rehash */ 32 int old_caps_proto[MAXCLICAPS]; /**< List of old CAP protocol values - used for /rehash */ 33 34 MODVAR ClientCapability *clicaps = NULL; /* List of client capabilities */ 35 36 void clicap_init(void) 37 { 38 memset(&old_caps, 0, sizeof(old_caps)); 39 } 40 41 /** 42 * Returns an clicap handle based on the given token name. 43 * 44 * @param token The clicap token to search for. 45 * @return Returns the handle to the clicap token if it was found, 46 * otherwise NULL is returned. 47 */ 48 ClientCapability *ClientCapabilityFindReal(const char *token) 49 { 50 ClientCapability *clicap; 51 52 for (clicap = clicaps; clicap; clicap = clicap->next) 53 { 54 if (!strcasecmp(token, clicap->name)) 55 return clicap; 56 } 57 58 return NULL; 59 } 60 61 /** 62 * Returns an clicap handle based on the given token name. 63 * 64 * @param token The clicap token to search for. 65 * @return Returns the handle to the clicap token if it was found, 66 * otherwise NULL is returned. 67 */ 68 ClientCapability *ClientCapabilityFind(const char *token, Client *client) 69 { 70 ClientCapability *clicap; 71 72 for (clicap = clicaps; clicap; clicap = clicap->next) 73 { 74 if (!strcasecmp(token, clicap->name)) 75 { 76 if (clicap->visible && !clicap->visible(client)) 77 return NULL; /* hidden */ 78 return clicap; 79 } 80 } 81 return NULL; 82 } 83 84 /** Find the bit that will be set if 'token' is enabled */ 85 long ClientCapabilityBit(const char *token) 86 { 87 ClientCapability *clicap = ClientCapabilityFindReal(token); 88 89 #ifdef DEBUGMODE 90 if (!clicap) 91 { 92 unreal_log(ULOG_WARNING, "main", "BUG_CLIENTCAPABILITYBIT_UNKNOWN_TOKEN", NULL, 93 "[BUG] ClientCapabilityBit() check for unknown token: $token", 94 log_data_string("token", token)); 95 } 96 #endif 97 98 return clicap ? clicap->cap : 0L; 99 } 100 101 void SetCapability(Client *client, const char *token) 102 { 103 client->local->caps |= ClientCapabilityBit(token); 104 } 105 106 void ClearCapability(Client *client, const char *token) 107 { 108 client->local->caps &= ~(ClientCapabilityBit(token)); 109 } 110 111 long clicap_allocate_cap(void) 112 { 113 long v; 114 ClientCapability *clicap; 115 116 /* The first bit (v=1) is used by the "invert" marker */ 117 for (v=2; v; v <<= 1) 118 { 119 unsigned char found = 0; 120 for (clicap = clicaps; clicap; clicap = clicap->next) 121 { 122 if (clicap->cap == v) 123 { 124 found = 1; 125 break; 126 } 127 } 128 if (!found) 129 return v; /* free bit found */ 130 } 131 132 return 0; 133 } 134 135 /** 136 * Adds a new clicap token. 137 * 138 * @param module The module which owns this token. 139 * @param clicap_request The details of the requested token, handlers, etc. 140 * @param cap The assigned capability bit. 141 * @return Returns the handle to the new token if successful, otherwise NULL. 142 * The module's error code contains specific information about the 143 * error. 144 */ 145 ClientCapability *ClientCapabilityAdd(Module *module, ClientCapabilityInfo *clicap_request, long *cap) 146 { 147 ClientCapability *clicap; 148 int exists = 0; 149 150 if (cap) 151 *cap = 0; /* Initialize early */ 152 153 clicap = ClientCapabilityFindReal(clicap_request->name); 154 if (clicap) 155 { 156 exists = 1; 157 if (clicap->unloaded) 158 { 159 clicap->unloaded = 0; 160 } else { 161 if (module) 162 module->errorcode = MODERR_EXISTS; 163 return NULL; 164 } 165 } else { 166 long v = 0; 167 168 /* Allocate a bit, but only if the module needs it. 169 * (some clicaps are advertise-only and never gets set, 170 * hence they don't need a bit allocated to them) 171 */ 172 if (cap != NULL) 173 { 174 v = clicap_allocate_cap(); 175 if (v == 0) 176 { 177 unreal_log(ULOG_ERROR, "module", "CLIENTCAPABILITY_OUT_OF_SPACE", NULL, 178 "ClientCapabilityAdd: out of space!!!"); 179 if (module) 180 module->errorcode = MODERR_NOSPACE; 181 return NULL; 182 } 183 } 184 /* New client capability */ 185 clicap = safe_alloc(sizeof(ClientCapability)); 186 safe_strdup(clicap->name, clicap_request->name); 187 clicap->cap = v; 188 } 189 /* Add or update the following fields: */ 190 clicap->owner = module; 191 clicap->flags = clicap_request->flags; 192 clicap->visible = clicap_request->visible; 193 clicap->parameter = clicap_request->parameter; 194 195 if (!exists) 196 AddListItem(clicap, clicaps); 197 198 if (clicap->cap && !cap) 199 abort(); /* module API call error */ 200 201 if (cap) 202 *cap = clicap->cap; 203 204 if (module) 205 { 206 ModuleObject *clicapobj = safe_alloc(sizeof(ModuleObject)); 207 clicapobj->object.clicap = clicap; 208 clicapobj->type = MOBJ_CLICAP; 209 AddListItem(clicapobj, module->objects); 210 module->errorcode = MODERR_NOERROR; 211 } 212 213 return clicap; 214 } 215 216 void unload_clicap_commit(ClientCapability *clicap) 217 { 218 /* This is an unusual operation, I think we should log it. */ 219 unreal_log(ULOG_INFO, "module", "UNLOAD_CLICAP", NULL, 220 "Unloading client capability '$token'", 221 log_data_string("token", clicap->name)); 222 223 /* NOTE: Stripping the CAP from local clients is done 224 * in clicap_check_for_changes(), so not here. 225 */ 226 227 /* A message tag handler may depend on us, remove it */ 228 /* NOTE: This assumes there is a 0:1 or 1:1 relationship between 229 * the two, but in theory there could be multiple message tags 230 * introduced by 1 capability. Ah well, we'll cross that 231 * bridge when we come to it ;) 232 */ 233 if (clicap->mtag_handler) 234 clicap->mtag_handler->clicap_handler = NULL; 235 236 /* Destroy the capability */ 237 DelListItem(clicap, clicaps); 238 safe_free(clicap->name); 239 safe_free(clicap); 240 } 241 /** 242 * Removes the specified clicap token. 243 * 244 * @param clicap The token to remove. 245 */ 246 void ClientCapabilityDel(ClientCapability *clicap) 247 { 248 if (clicap->owner) 249 { 250 ModuleObject *mobj; 251 for (mobj = clicap->owner->objects; mobj; mobj = mobj->next) { 252 if (mobj->type == MOBJ_CLICAP && mobj->object.clicap == clicap) { 253 DelListItem(mobj, clicap->owner->objects); 254 safe_free(mobj); 255 break; 256 } 257 } 258 clicap->owner = NULL; 259 } 260 261 if (loop.rehashing) 262 clicap->unloaded = 1; 263 else 264 unload_clicap_commit(clicap); 265 } 266 267 void unload_all_unused_caps(void) 268 { 269 ClientCapability *clicap, *clicap_next; 270 271 for (clicap = clicaps; clicap; clicap = clicap_next) 272 { 273 clicap_next = clicap->next; 274 if (clicap->unloaded) 275 unload_clicap_commit(clicap); 276 } 277 } 278 279 /** Called before REHASH. This saves the list of cap names and protocol values */ 280 void clicap_pre_rehash(void) 281 { 282 ClientCapability *clicap; 283 int i = 0; 284 285 for (i=0; i < MAXCLICAPS; i++) 286 { 287 safe_free(old_caps[i]); 288 old_caps_proto[i] = 0; 289 } 290 291 for (i=0, clicap = clicaps; clicap; clicap = clicap->next) 292 { 293 if (i == MAXCLICAPS) 294 { 295 unreal_log(ULOG_ERROR, "module", "BUG_TOO_MANY_CLIENTCAPABILITIES", NULL, 296 "[BUG] clicap_pre_rehash: More than $count caps loaded - this should never happen", 297 log_data_integer("count", MAXCLICAPS)); 298 break; 299 } 300 safe_strdup(old_caps[i], clicap->name); 301 old_caps_proto[i] = clicap->cap; 302 i++; 303 } 304 } 305 306 /** Clear 'proto' protocol for all users */ 307 void clear_cap_for_users(long cap) 308 { 309 Client *client; 310 311 if (cap == 0) 312 return; 313 314 list_for_each_entry(client, &lclient_list, lclient_node) 315 { 316 client->local->caps &= ~cap; 317 } 318 list_for_each_entry(client, &unknown_list, lclient_node) 319 { 320 client->local->caps &= ~cap; 321 } 322 } 323 324 /** Called after REHASH. This will deal with: 325 * 1. Clearing flags for caps that are deleted 326 * 2. Sending any CAP DEL 327 * 3. Sending any CAP NEW 328 */ 329 void clicap_check_for_changes(void) 330 { 331 ClientCapability *clicap; 332 char *name; 333 int i; 334 int found; 335 336 if (!loop.rehashing) 337 return; /* First boot */ 338 339 /* Let's deal with CAP DEL first: 340 * Go through the old caps and see what's missing now. 341 */ 342 for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) 343 { 344 name = old_caps[i]; 345 found = 0; 346 for (clicap = clicaps; clicap; clicap = clicap->next) 347 { 348 if (!strcmp(clicap->name, name)) 349 { 350 found = 1; 351 break; 352 } 353 } 354 if (!found) 355 { 356 /* Broadcast CAP DEL to local users */ 357 send_cap_notify(0, name); 358 clear_cap_for_users(old_caps_proto[i]); 359 } 360 } 361 362 /* Now deal with CAP ADD: 363 * Go through the new caps and see if it was missing from old caps. 364 */ 365 for (clicap = clicaps; clicap; clicap = clicap->next) 366 { 367 name = clicap->name; 368 found = 0; 369 for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) 370 { 371 if (!strcmp(old_caps[i], name)) 372 { 373 found = 1; 374 break; 375 } 376 } 377 378 if (!found) 379 { 380 /* Broadcast CAP NEW to local users */ 381 send_cap_notify(1, name); 382 } 383 } 384 385 /* Now free the old caps. */ 386 for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) 387 safe_free(old_caps[i]); 388 }