unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
cap.c (8293B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/cap.c 3 * (C) 2012 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 typedef int (*bqcmp)(const void *, const void *); 26 27 CMD_FUNC(cmd_cap); 28 29 #define MSG_CAP "CAP" 30 31 ModuleHeader MOD_HEADER 32 = { 33 "cap", /* Name of module */ 34 "5.0", /* Version */ 35 "command /cap", /* Short description of module */ 36 "UnrealIRCd Team", 37 "unrealircd-6", 38 }; 39 40 /* Forward declarations */ 41 int cap_is_handshake_finished(Client *client); 42 int cap_never_visible(Client *client); 43 44 /* Variables */ 45 long CAP_IN_PROGRESS = 0L; 46 long CAP_NOTIFY = 0L; 47 48 MOD_INIT() 49 { 50 ClientCapabilityInfo c; 51 52 MARK_AS_OFFICIAL_MODULE(modinfo); 53 CommandAdd(modinfo->handle, MSG_CAP, cmd_cap, MAXPARA, CMD_UNREGISTERED|CMD_USER|CMD_NOLAG); 54 55 /* This first cap is special, in the sense that it is hidden 56 * and indicates a cap exchange is in progress. 57 */ 58 memset(&c, 0, sizeof(c)); 59 c.name = "cap"; 60 c.visible = cap_never_visible; 61 ClientCapabilityAdd(modinfo->handle, &c, &CAP_IN_PROGRESS); 62 63 memset(&c, 0, sizeof(c)); 64 c.name = "cap-notify"; 65 ClientCapabilityAdd(modinfo->handle, &c, &CAP_NOTIFY); 66 67 HookAdd(modinfo->handle, HOOKTYPE_IS_HANDSHAKE_FINISHED, 0, cap_is_handshake_finished); 68 69 return MOD_SUCCESS; 70 } 71 72 MOD_LOAD() 73 { 74 return MOD_SUCCESS; 75 } 76 77 MOD_UNLOAD() 78 { 79 return MOD_SUCCESS; 80 } 81 82 static ClientCapability *clicap_find(Client *client, const char *data, int *negate, int *finished, int *errors) 83 { 84 static char buf[BUFSIZE]; 85 static char *p; 86 ClientCapability *cap; 87 char *s; 88 89 *negate = 0; 90 91 if (data) 92 { 93 strlcpy(buf, data, sizeof(buf)); 94 p = buf; 95 *finished = 0; 96 *errors = 0; 97 } 98 99 if (*finished) 100 return NULL; 101 102 /* skip any whitespace */ 103 while(*p && isspace(*p)) 104 p++; 105 106 if (BadPtr(p)) 107 { 108 *finished = 1; 109 return NULL; 110 } 111 112 if (*p == '-') 113 { 114 *negate = 1; 115 p++; 116 117 /* someone sent a '-' without a parameter.. */ 118 if (*p == '\0') 119 return NULL; 120 } 121 122 if ((s = strchr(p, ' '))) 123 *s++ = '\0'; 124 125 cap = ClientCapabilityFind(p, client); 126 if (!s) 127 *finished = 1; 128 129 p = s; /* point to next token for next iteration */ 130 131 if (cap && (cap->flags & CLICAP_FLAGS_ADVERTISE_ONLY)) 132 cap = NULL; 133 134 if (!cap) 135 *errors = 1; 136 137 return cap; 138 } 139 140 static void clicap_generate(Client *client, const char *subcmd, int flags) 141 { 142 ClientCapability *cap; 143 char buf[BUFSIZE]; 144 char capbuf[BUFSIZE]; 145 char *p; 146 int buflen = 0; 147 int curlen, mlen; 148 149 mlen = snprintf(buf, BUFSIZE, ":%s CAP %s %s", me.name, BadPtr(client->name) ? "*" : client->name, subcmd); 150 151 p = capbuf; 152 buflen = mlen; 153 154 if (flags == -1) 155 { 156 sendto_one(client, NULL, "%s :", buf); 157 return; 158 } 159 160 for (cap = clicaps; cap; cap = cap->next) 161 { 162 char name[256]; 163 const char *param; 164 165 if (cap->visible && !cap->visible(client)) 166 continue; /* hidden */ 167 168 if (flags) 169 { 170 if (!cap->cap || !(client->local->caps & cap->cap)) 171 continue; 172 } 173 174 if ((client->local->cap_protocol >= 302) && cap->parameter && (param = cap->parameter(client))) 175 snprintf(name, sizeof(name), "%s=%s", cap->name, param); 176 else 177 strlcpy(name, cap->name, sizeof(name)); 178 179 /* \r\n\0, possible "-~=", space, " *" */ 180 if (buflen + strlen(name) >= BUFSIZE - 10) 181 { 182 if (buflen != mlen) 183 *(p - 1) = '\0'; 184 else 185 *p = '\0'; 186 187 sendto_one(client, NULL, "%s * :%s", buf, capbuf); 188 p = capbuf; 189 buflen = mlen; 190 } 191 192 curlen = snprintf(p, (capbuf + BUFSIZE) - p, "%s ", name); 193 p += curlen; 194 buflen += curlen; 195 } 196 197 if (buflen != mlen) 198 *(p - 1) = '\0'; 199 else 200 *p = '\0'; 201 202 sendto_one(client, NULL, "%s :%s", buf, capbuf); 203 } 204 205 static void cap_end(Client *client, const char *arg) 206 { 207 if (IsUser(client)) 208 return; 209 210 ClearCapabilityFast(client, CAP_IN_PROGRESS); 211 212 if (*client->name && client->user && *client->user->username && IsNotSpoof(client)) 213 register_user(client); 214 } 215 216 static void cap_list(Client *client, const char *arg) 217 { 218 clicap_generate(client, "LIST", client->local->caps ? client->local->caps : -1); 219 } 220 221 static void cap_ls(Client *client, const char *arg) 222 { 223 if (!IsUser(client)) 224 SetCapabilityFast(client, CAP_IN_PROGRESS); 225 226 if (arg) 227 client->local->cap_protocol = atoi(arg); 228 229 /* Since the client did a "CAP LS" it apparently supports CAP 230 * and thus at least protocol version 300. 231 */ 232 if (client->local->cap_protocol < 300) 233 client->local->cap_protocol = 300; 234 235 if (client->local->cap_protocol >= 302) 236 SetCapabilityFast(client, CAP_NOTIFY); /* Implicit support (JIT) */ 237 238 clicap_generate(client, "LS", 0); 239 } 240 241 static void cap_req(Client *client, const char *arg) 242 { 243 char buf[BUFSIZE]; 244 char pbuf[2][BUFSIZE]; 245 ClientCapability *cap; 246 int buflen, plen; 247 int i = 0; 248 int capadd = 0, capdel = 0; 249 int finished = 0, negate; 250 int errors = 0; 251 252 if (!IsUser(client)) 253 SetCapabilityFast(client, CAP_IN_PROGRESS); 254 255 if (BadPtr(arg)) 256 return; 257 258 buflen = snprintf(buf, sizeof(buf), ":%s CAP %s ACK", 259 me.name, BadPtr(client->name) ? "*" : client->name); 260 261 pbuf[0][0] = '\0'; 262 plen = 0; 263 264 for(cap = clicap_find(client, arg, &negate, &finished, &errors); cap; 265 cap = clicap_find(client, NULL, &negate, &finished, &errors)) 266 { 267 /* filled the first array, but cant send it in case the 268 * request fails. one REQ should never fill more than two 269 * buffers --fl 270 */ 271 if (buflen + plen + strlen(cap->name) + 6 >= BUFSIZE) 272 { 273 pbuf[1][0] = '\0'; 274 plen = 0; 275 i = 1; 276 } 277 278 if (negate) 279 { 280 strcat(pbuf[i], "-"); 281 plen++; 282 283 capdel |= cap->cap; 284 } 285 else 286 { 287 capadd |= cap->cap; 288 } 289 290 strcat(pbuf[i], cap->name); 291 strcat(pbuf[i], " "); 292 plen += (strlen(cap->name) + 1); 293 } 294 295 /* This one is special */ 296 if ((client->local->cap_protocol >= 302) && (capdel & CAP_NOTIFY)) 297 errors++; /* Reject "CAP REQ -cap-notify" */ 298 299 if (errors) 300 { 301 sendto_one(client, NULL, ":%s CAP %s NAK :%s", me.name, BadPtr(client->name) ? "*" : client->name, arg); 302 return; 303 } 304 305 if (i) 306 { 307 sendto_one(client, NULL, "%s * :%s", buf, pbuf[0]); 308 sendto_one(client, NULL, "%s :%s", buf, pbuf[1]); 309 } 310 else 311 sendto_one(client, NULL, "%s :%s", buf, pbuf[0]); 312 313 client->local->caps |= capadd; 314 client->local->caps &= ~capdel; 315 } 316 317 struct clicap_cmd { 318 const char *cmd; 319 void (*func)(Client *source_p, const char *arg); 320 }; 321 322 static struct clicap_cmd clicap_cmdtable[] = { 323 { "END", cap_end }, 324 { "LIST", cap_list }, 325 { "LS", cap_ls }, 326 { "REQ", cap_req }, 327 }; 328 329 static int clicap_cmd_search(const char *command, struct clicap_cmd *entry) 330 { 331 return strcasecmp(command, entry->cmd); 332 } 333 334 int cap_never_visible(Client *client) 335 { 336 return 0; 337 } 338 339 /** Is our handshake done? */ 340 int cap_is_handshake_finished(Client *client) 341 { 342 if (HasCapabilityFast(client, CAP_IN_PROGRESS)) 343 return 0; /* We are in CAP LS stage, waiting for a CAP END */ 344 345 return 1; 346 } 347 348 CMD_FUNC(cmd_cap) 349 { 350 struct clicap_cmd *cmd; 351 352 if (!MyConnect(client)) 353 return; 354 355 /* CAP is marked as "no fake lag" because we use custom fake lag rules: 356 * Only add a 1 second fake lag penalty if this is the XXth command. 357 * This will speed up connections considerably. 358 */ 359 if (client->local->traffic.messages_received > 15) 360 add_fake_lag(client, 1000); 361 362 if (DISABLE_CAP) 363 { 364 /* I know nothing! */ 365 if (IsUser(client)) 366 sendnumeric(client, ERR_UNKNOWNCOMMAND, "CAP"); 367 else 368 sendnumeric(client, ERR_NOTREGISTERED); 369 return; 370 } 371 372 if (parc < 2) 373 { 374 sendnumeric(client, ERR_NEEDMOREPARAMS, "CAP"); 375 376 return; 377 } 378 379 if (!(cmd = bsearch(parv[1], clicap_cmdtable, 380 sizeof(clicap_cmdtable) / sizeof(struct clicap_cmd), 381 sizeof(struct clicap_cmd), (bqcmp) clicap_cmd_search))) 382 { 383 sendnumeric(client, ERR_INVALIDCAPCMD, parv[1]); 384 385 return; 386 } 387 388 cmd->func(client, parv[2]); 389 }