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 }