]> jfr.im git - solanum.git/blame - modules/m_cap.c
cap: fix compile
[solanum.git] / modules / m_cap.c
CommitLineData
212380e3 1/* modules/m_cap.c
55abcbb2 2 *
212380e3
AC
3 * Copyright (C) 2005 Lee Hardy <lee@leeh.co.uk>
4 * Copyright (C) 2005 ircd-ratbox development team
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * 1.Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2.Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3.The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $Id: m_cap.c 676 2006-02-03 20:05:09Z gxti $
31 */
32
33#include "stdinc.h"
212380e3
AC
34#include "class.h"
35#include "client.h"
4562c604 36#include "match.h"
212380e3
AC
37#include "ircd.h"
38#include "numeric.h"
39#include "msg.h"
40#include "parse.h"
41#include "modules.h"
42#include "s_serv.h"
43#include "s_user.h"
77d3d2db 44#include "send.h"
212380e3
AC
45
46typedef int (*bqcmp)(const void *, const void *);
47
48static int m_cap(struct Client *, struct Client *, int, const char **);
49static int modinit(void);
50
51struct Message cap_msgtab = {
52 "CAP", 0, 0, 0, MFLG_SLOW,
53 {{m_cap, 2}, {m_cap, 2}, mg_ignore, mg_ignore, mg_ignore, {m_cap, 2}}
54};
55
56mapi_clist_av1 cap_clist[] = { &cap_msgtab, NULL };
57DECLARE_MODULE_AV1(cap, modinit, NULL, cap_clist, NULL, NULL, "$Revision: 676 $");
58
0044d400
AC
59#define _CLICAP(name, capserv, capclient, caprequired, flags) \
60 { (name), (capserv), (capclient), (caprequired), (flags), sizeof(name) - 1 }
212380e3
AC
61
62#define CLICAP_FLAGS_STICKY 0x001
63
64static struct clicap
65{
66 const char *name;
67 int cap_serv; /* for altering s->c */
68 int cap_cli; /* for altering c->s */
0044d400 69 int cap_required_serv; /* required dependency cap */
212380e3
AC
70 int flags;
71 int namelen;
72} clicap_list[] = {
0044d400
AC
73 _CLICAP("multi-prefix", CLICAP_MULTI_PREFIX, 0, 0, 0),
74 _CLICAP("sasl", CLICAP_SASL, 0, 0, 0),
ef3ab8e3 75 _CLICAP("sasl-reauth", CLICAP_SASL_REAUTH, 0, CLICAP_SASL, 0),
0044d400
AC
76 _CLICAP("account-notify", CLICAP_ACCOUNT_NOTIFY, 0, 0, 0),
77 _CLICAP("extended-join", CLICAP_EXTENDED_JOIN, 0, 0, 0),
78 _CLICAP("away-notify", CLICAP_AWAY_NOTIFY, 0, 0, 0),
79 _CLICAP("tls", CLICAP_TLS, 0, 0, 0),
212380e3
AC
80};
81
82#define CLICAP_LIST_LEN (sizeof(clicap_list) / sizeof(struct clicap))
83
84static int clicap_sort(struct clicap *, struct clicap *);
85
86static int
87modinit(void)
88{
89 qsort(clicap_list, CLICAP_LIST_LEN, sizeof(struct clicap),
90 (bqcmp) clicap_sort);
91 return 0;
92}
93
94static int
95clicap_sort(struct clicap *one, struct clicap *two)
96{
97 return irccmp(one->name, two->name);
98}
99
100static int
101clicap_compare(const char *name, struct clicap *cap)
102{
103 return irccmp(name, cap->name);
104}
105
106/* clicap_find()
107 * Used iteratively over a buffer, extracts individual cap tokens.
108 *
109 * Inputs: buffer to start iterating over (NULL to iterate over existing buf)
110 * int pointer to whether the cap token is negated
111 * int pointer to whether we finish with success
112 * Ouputs: Cap entry if found, NULL otherwise.
113 */
114static struct clicap *
115clicap_find(const char *data, int *negate, int *finished)
116{
117 static char buf[BUFSIZE];
118 static char *p;
119 struct clicap *cap;
120 char *s;
121
122 *negate = 0;
123
124 if(data)
125 {
f427c8b0 126 rb_strlcpy(buf, data, sizeof(buf));
212380e3
AC
127 p = buf;
128 }
129
130 if(*finished)
131 return NULL;
132
133 /* skip any whitespace */
134 while(*p && IsSpace(*p))
135 p++;
136
137 if(EmptyString(p))
138 {
139 *finished = 1;
140 return NULL;
141 }
142
143 if(*p == '-')
144 {
145 *negate = 1;
146 p++;
147
148 /* someone sent a '-' without a parameter.. */
149 if(*p == '\0')
150 return NULL;
151 }
152
153 if((s = strchr(p, ' ')))
154 *s++ = '\0';
155
55abcbb2 156 if((cap = bsearch(p, clicap_list, CLICAP_LIST_LEN,
212380e3
AC
157 sizeof(struct clicap), (bqcmp) clicap_compare)))
158 {
159 if(s)
160 p = s;
161 else
162 *finished = 1;
163 }
164
165 return cap;
166}
167
168/* clicap_generate()
169 * Generates a list of capabilities.
170 *
171 * Inputs: client to send to, subcmd to send,
172 * flags to match against: 0 to do none, -1 if client has no flags,
173 * int to whether we are doing CAP CLEAR
174 * Outputs: None
175 */
176static void
177clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clear)
178{
179 char buf[BUFSIZE];
180 char capbuf[BUFSIZE];
181 char *p;
182 int buflen = 0;
183 int curlen, mlen;
34e02db6 184 size_t i;
212380e3 185
7cdb0a09 186 mlen = rb_sprintf(buf, ":%s CAP %s %s",
55abcbb2
KB
187 me.name,
188 EmptyString(source_p->name) ? "*" : source_p->name,
212380e3
AC
189 subcmd);
190
191 p = capbuf;
192 buflen = mlen;
193
194 /* shortcut, nothing to do */
195 if(flags == -1)
196 {
197 sendto_one(source_p, "%s :", buf);
198 return;
199 }
200
201 for(i = 0; i < CLICAP_LIST_LEN; i++)
202 {
203 if(flags)
204 {
205 if(!IsCapable(source_p, clicap_list[i].cap_serv))
206 continue;
207 /* they are capable of this, check sticky */
208 else if(clear && clicap_list[i].flags & CLICAP_FLAGS_STICKY)
209 continue;
210 }
211
212 /* \r\n\0, possible "-~=", space, " *" */
213 if(buflen + clicap_list[i].namelen >= BUFSIZE - 10)
214 {
215 /* remove our trailing space -- if buflen == mlen
216 * here, we didnt even succeed in adding one.
217 */
218 if(buflen != mlen)
219 *(p - 1) = '\0';
220 else
221 *p = '\0';
222
223 sendto_one(source_p, "%s * :%s", buf, capbuf);
224 p = capbuf;
225 buflen = mlen;
226 }
227
228 if(clear)
229 {
230 *p++ = '-';
231 buflen++;
232
233 /* needs a client ack */
55abcbb2 234 if(clicap_list[i].cap_cli &&
212380e3
AC
235 IsCapable(source_p, clicap_list[i].cap_cli))
236 {
237 *p++ = '~';
238 buflen++;
239 }
240 }
241 else
242 {
243 if(clicap_list[i].flags & CLICAP_FLAGS_STICKY)
244 {
245 *p++ = '=';
246 buflen++;
247 }
248
249 /* if we're doing an LS, then we only send this if
250 * they havent ack'd
251 */
252 if(clicap_list[i].cap_cli &&
253 (!flags || !IsCapable(source_p, clicap_list[i].cap_cli)))
254 {
255 *p++ = '~';
256 buflen++;
257 }
258 }
259
7cdb0a09 260 curlen = rb_sprintf(p, "%s ", clicap_list[i].name);
212380e3
AC
261 p += curlen;
262 buflen += curlen;
263 }
264
265 /* remove trailing space */
266 if(buflen != mlen)
267 *(p - 1) = '\0';
268 else
269 *p = '\0';
270
271 sendto_one(source_p, "%s :%s", buf, capbuf);
272}
273
274static void
275cap_ack(struct Client *source_p, const char *arg)
276{
277 struct clicap *cap;
278 int capadd = 0, capdel = 0;
279 int finished = 0, negate;
280
281 if(EmptyString(arg))
282 return;
283
284 for(cap = clicap_find(arg, &negate, &finished); cap;
285 cap = clicap_find(NULL, &negate, &finished))
286 {
287 /* sent an ACK for something they havent REQd */
288 if(!IsCapable(source_p, cap->cap_serv))
289 continue;
290
291 if(negate)
292 {
293 /* dont let them ack something sticky off */
294 if(cap->flags & CLICAP_FLAGS_STICKY)
295 continue;
296
297 capdel |= cap->cap_cli;
298 }
299 else
300 capadd |= cap->cap_cli;
301 }
302
303 source_p->localClient->caps |= capadd;
304 source_p->localClient->caps &= ~capdel;
305}
306
307static void
308cap_clear(struct Client *source_p, const char *arg)
309{
55abcbb2 310 clicap_generate(source_p, "ACK",
212380e3
AC
311 source_p->localClient->caps ? source_p->localClient->caps : -1, 1);
312
313 /* XXX - sticky capabs */
314#ifdef CLICAP_STICKY
315 source_p->localClient->caps = source_p->localClient->caps & CLICAP_STICKY;
316#else
317 source_p->localClient->caps = 0;
318#endif
319}
320
321static void
322cap_end(struct Client *source_p, const char *arg)
323{
324 if(IsRegistered(source_p))
325 return;
326
095328a7 327 source_p->flags &= ~FLAGS_CLICAP;
212380e3 328
1fb3b1e1 329 if(source_p->name[0] && source_p->flags & FLAGS_SENTUSER)
212380e3
AC
330 {
331 char buf[USERLEN+1];
f427c8b0 332 rb_strlcpy(buf, source_p->username, sizeof(buf));
212380e3
AC
333 register_local_user(source_p, source_p, buf);
334 }
335}
336
337static void
338cap_list(struct Client *source_p, const char *arg)
339{
340 /* list of what theyre currently using */
341 clicap_generate(source_p, "LIST",
342 source_p->localClient->caps ? source_p->localClient->caps : -1, 0);
343}
344
345static void
346cap_ls(struct Client *source_p, const char *arg)
347{
348 if(!IsRegistered(source_p))
095328a7 349 source_p->flags |= FLAGS_CLICAP;
212380e3
AC
350
351 /* list of what we support */
352 clicap_generate(source_p, "LS", 0, 0);
353}
354
355static void
356cap_req(struct Client *source_p, const char *arg)
357{
358 char buf[BUFSIZE];
359 char pbuf[2][BUFSIZE];
360 struct clicap *cap;
361 int buflen, plen;
362 int i = 0;
363 int capadd = 0, capdel = 0;
364 int finished = 0, negate;
365
366 if(!IsRegistered(source_p))
095328a7 367 source_p->flags |= FLAGS_CLICAP;
212380e3
AC
368
369 if(EmptyString(arg))
370 return;
371
7cdb0a09 372 buflen = rb_snprintf(buf, sizeof(buf), ":%s CAP %s ACK",
212380e3
AC
373 me.name, EmptyString(source_p->name) ? "*" : source_p->name);
374
375 pbuf[0][0] = '\0';
376 plen = 0;
377
378 for(cap = clicap_find(arg, &negate, &finished); cap;
379 cap = clicap_find(NULL, &negate, &finished))
380 {
381 /* filled the first array, but cant send it in case the
382 * request fails. one REQ should never fill more than two
383 * buffers --fl
384 */
385 if(buflen + plen + cap->namelen + 6 >= BUFSIZE)
386 {
387 pbuf[1][0] = '\0';
388 plen = 0;
389 i = 1;
390 }
391
392 if(negate)
393 {
394 if(cap->flags & CLICAP_FLAGS_STICKY)
395 {
396 finished = 0;
397 break;
398 }
399
400 strcat(pbuf[i], "-");
401 plen++;
402
403 capdel |= cap->cap_serv;
404 }
405 else
406 {
3a48406b 407 if(cap->cap_required_serv && !((capadd & cap->cap_required_serv) == cap->cap_required_serv || IsCapable(source_p, cap->cap_required_serv)))
0044d400
AC
408 {
409 finished = 0;
410 break;
411 }
412
212380e3
AC
413 if(cap->flags & CLICAP_FLAGS_STICKY)
414 {
415 strcat(pbuf[i], "=");
416 plen++;
417 }
418
419 capadd |= cap->cap_serv;
420 }
421
422 if(cap->cap_cli)
423 {
424 strcat(pbuf[i], "~");
425 plen++;
426 }
427
428 strcat(pbuf[i], cap->name);
429 strcat(pbuf[i], " ");
430 plen += (cap->namelen + 1);
431 }
432
433 if(!finished)
434 {
435 sendto_one(source_p, ":%s CAP %s NAK :%s",
436 me.name, EmptyString(source_p->name) ? "*" : source_p->name, arg);
437 return;
438 }
439
440 if(i)
441 {
442 sendto_one(source_p, "%s * :%s", buf, pbuf[0]);
443 sendto_one(source_p, "%s :%s", buf, pbuf[1]);
444 }
445 else
446 sendto_one(source_p, "%s :%s", buf, pbuf[0]);
447
448 source_p->localClient->caps |= capadd;
449 source_p->localClient->caps &= ~capdel;
450}
451
452static struct clicap_cmd
453{
454 const char *cmd;
455 void (*func)(struct Client *source_p, const char *arg);
456} clicap_cmdlist[] = {
457 /* This list *MUST* be in alphabetical order */
458 { "ACK", cap_ack },
459 { "CLEAR", cap_clear },
460 { "END", cap_end },
461 { "LIST", cap_list },
462 { "LS", cap_ls },
463 { "REQ", cap_req },
464};
465
466static int
467clicap_cmd_search(const char *command, struct clicap_cmd *entry)
468{
469 return irccmp(command, entry->cmd);
470}
471
472static int
473m_cap(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
474{
475 struct clicap_cmd *cmd;
476
477 if(!(cmd = bsearch(parv[1], clicap_cmdlist,
478 sizeof(clicap_cmdlist) / sizeof(struct clicap_cmd),
479 sizeof(struct clicap_cmd), (bqcmp) clicap_cmd_search)))
480 {
481 sendto_one(source_p, form_str(ERR_INVALIDCAPCMD),
48a038f4
JT
482 me.name, EmptyString(source_p->name) ? "*" : source_p->name,
483 parv[1]);
212380e3
AC
484 return 0;
485 }
486
487 (cmd->func)(source_p, parv[2]);
488 return 0;
489}