]>
jfr.im git - solanum.git/blob - modules/m_cap.c
3 * Copyright (C) 2005 Lee Hardy <lee@leeh.co.uk>
4 * Copyright (C) 2005 ircd-ratbox development team
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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.
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.
30 * $Id: m_cap.c 676 2006-02-03 20:05:09Z gxti $
48 typedef int (*bqcmp
)(const void *, const void *);
50 static int m_cap(struct Client
*, struct Client
*, int, const char **);
51 static int modinit(void);
53 struct Message cap_msgtab
= {
54 "CAP", 0, 0, 0, MFLG_SLOW
,
55 {{m_cap
, 2}, {m_cap
, 2}, mg_ignore
, mg_ignore
, mg_ignore
, {m_cap
, 2}}
58 mapi_clist_av1 cap_clist
[] = { &cap_msgtab
, NULL
};
59 DECLARE_MODULE_AV1(cap
, modinit
, NULL
, cap_clist
, NULL
, NULL
, "$Revision: 676 $");
61 #define _CLICAP(name, capserv, capclient, caprequired, flags) \
62 { (name), (capserv), (capclient), (caprequired), (flags), sizeof(name) - 1 }
64 #define CLICAP_FLAGS_STICKY 0x001
69 int cap_serv
; /* for altering s->c */
70 int cap_cli
; /* for altering c->s */
71 int cap_required_serv
; /* required dependency cap */
75 _CLICAP("multi-prefix", CLICAP_MULTI_PREFIX
, 0, 0, 0),
76 _CLICAP("sasl", CLICAP_SASL
, 0, 0, 0),
77 _CLICAP("account-notify", CLICAP_ACCOUNT_NOTIFY
, 0, 0, 0),
78 _CLICAP("extended-join", CLICAP_EXTENDED_JOIN
, 0, 0, 0),
79 _CLICAP("away-notify", CLICAP_AWAY_NOTIFY
, 0, 0, 0),
80 _CLICAP("tls", CLICAP_TLS
, 0, 0, 0),
81 _CLICAP("userhost-in-names", CLICAP_USERHOST_IN_NAMES
, 0, 0, 0),
82 _CLICAP("cap-notify", CLICAP_CAP_NOTIFY
, 0, 0, 0),
85 #define CLICAP_LIST_LEN (sizeof(clicap_list) / sizeof(struct clicap))
87 static int clicap_sort(struct clicap
*, struct clicap
*);
92 qsort(clicap_list
, CLICAP_LIST_LEN
, sizeof(struct clicap
),
98 clicap_sort(struct clicap
*one
, struct clicap
*two
)
100 return irccmp(one
->name
, two
->name
);
104 clicap_compare(const char *name
, struct clicap
*cap
)
106 return irccmp(name
, cap
->name
);
110 * Used iteratively over a buffer, extracts individual cap tokens.
112 * Inputs: buffer to start iterating over (NULL to iterate over existing buf)
113 * int pointer to whether the cap token is negated
114 * int pointer to whether we finish with success
115 * Ouputs: Cap entry if found, NULL otherwise.
117 static struct clicap
*
118 clicap_find(const char *data
, int *negate
, int *finished
)
120 static char buf
[BUFSIZE
];
129 rb_strlcpy(buf
, data
, sizeof(buf
));
136 /* skip any whitespace */
137 while(*p
&& IsSpace(*p
))
151 /* someone sent a '-' without a parameter.. */
156 if((s
= strchr(p
, ' ')))
159 if((cap
= bsearch(p
, clicap_list
, CLICAP_LIST_LEN
,
160 sizeof(struct clicap
), (bqcmp
) clicap_compare
)))
172 * Generates a list of capabilities.
174 * Inputs: client to send to, subcmd to send,
175 * flags to match against: 0 to do none, -1 if client has no flags,
176 * int to whether we are doing CAP CLEAR
180 clicap_generate(struct Client
*source_p
, const char *subcmd
, int flags
, int clear
)
183 char capbuf
[BUFSIZE
];
189 mlen
= rb_sprintf(buf
, ":%s CAP %s %s",
191 EmptyString(source_p
->name
) ? "*" : source_p
->name
,
197 /* shortcut, nothing to do */
200 sendto_one(source_p
, "%s :", buf
);
204 for(i
= 0; i
< CLICAP_LIST_LEN
; i
++)
208 if(!IsCapable(source_p
, clicap_list
[i
].cap_serv
))
210 /* they are capable of this, check sticky */
211 else if(clear
&& clicap_list
[i
].flags
& CLICAP_FLAGS_STICKY
)
215 if (clicap_list
[i
].cap_serv
== CLICAP_SASL
)
217 struct Client
*agent_p
= NULL
;
219 if (!ConfigFileEntry
.sasl_service
)
222 agent_p
= find_named_client(ConfigFileEntry
.sasl_service
);
223 if (agent_p
== NULL
|| !IsService(agent_p
))
227 /* \r\n\0, possible "-~=", space, " *" */
228 if(buflen
+ clicap_list
[i
].namelen
>= BUFSIZE
- 10)
230 /* remove our trailing space -- if buflen == mlen
231 * here, we didnt even succeed in adding one.
238 sendto_one(source_p
, "%s * :%s", buf
, capbuf
);
248 /* needs a client ack */
249 if(clicap_list
[i
].cap_cli
&&
250 IsCapable(source_p
, clicap_list
[i
].cap_cli
))
258 if(clicap_list
[i
].flags
& CLICAP_FLAGS_STICKY
)
264 /* if we're doing an LS, then we only send this if
267 if(clicap_list
[i
].cap_cli
&&
268 (!flags
|| !IsCapable(source_p
, clicap_list
[i
].cap_cli
)))
275 curlen
= rb_sprintf(p
, "%s ", clicap_list
[i
].name
);
280 /* remove trailing space */
286 sendto_one(source_p
, "%s :%s", buf
, capbuf
);
290 cap_ack(struct Client
*source_p
, const char *arg
)
293 int capadd
= 0, capdel
= 0;
294 int finished
= 0, negate
;
299 for(cap
= clicap_find(arg
, &negate
, &finished
); cap
;
300 cap
= clicap_find(NULL
, &negate
, &finished
))
302 /* sent an ACK for something they havent REQd */
303 if(!IsCapable(source_p
, cap
->cap_serv
))
308 /* dont let them ack something sticky off */
309 if(cap
->flags
& CLICAP_FLAGS_STICKY
)
312 capdel
|= cap
->cap_cli
;
315 capadd
|= cap
->cap_cli
;
318 source_p
->localClient
->caps
|= capadd
;
319 source_p
->localClient
->caps
&= ~capdel
;
323 cap_clear(struct Client
*source_p
, const char *arg
)
325 clicap_generate(source_p
, "ACK",
326 source_p
->localClient
->caps
? source_p
->localClient
->caps
: -1, 1);
328 /* XXX - sticky capabs */
330 source_p
->localClient
->caps
= source_p
->localClient
->caps
& CLICAP_STICKY
;
332 source_p
->localClient
->caps
= 0;
337 cap_end(struct Client
*source_p
, const char *arg
)
339 if(IsRegistered(source_p
))
342 source_p
->flags
&= ~FLAGS_CLICAP
;
344 if(source_p
->name
[0] && source_p
->flags
& FLAGS_SENTUSER
)
347 rb_strlcpy(buf
, source_p
->username
, sizeof(buf
));
348 register_local_user(source_p
, source_p
, buf
);
353 cap_list(struct Client
*source_p
, const char *arg
)
355 /* list of what theyre currently using */
356 clicap_generate(source_p
, "LIST",
357 source_p
->localClient
->caps
? source_p
->localClient
->caps
: -1, 0);
361 cap_ls(struct Client
*source_p
, const char *arg
)
363 if(!IsRegistered(source_p
))
364 source_p
->flags
|= FLAGS_CLICAP
;
366 /* list of what we support */
367 clicap_generate(source_p
, "LS", 0, 0);
371 cap_req(struct Client
*source_p
, const char *arg
)
374 char pbuf
[2][BUFSIZE
];
378 int capadd
= 0, capdel
= 0;
379 int finished
= 0, negate
;
381 if(!IsRegistered(source_p
))
382 source_p
->flags
|= FLAGS_CLICAP
;
387 buflen
= rb_snprintf(buf
, sizeof(buf
), ":%s CAP %s ACK",
388 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
);
393 for(cap
= clicap_find(arg
, &negate
, &finished
); cap
;
394 cap
= clicap_find(NULL
, &negate
, &finished
))
396 /* filled the first array, but cant send it in case the
397 * request fails. one REQ should never fill more than two
400 if(buflen
+ plen
+ cap
->namelen
+ 6 >= BUFSIZE
)
409 if(cap
->flags
& CLICAP_FLAGS_STICKY
)
415 strcat(pbuf
[i
], "-");
418 capdel
|= cap
->cap_serv
;
422 if(cap
->cap_required_serv
&& !((capadd
& cap
->cap_required_serv
) == cap
->cap_required_serv
|| IsCapable(source_p
, cap
->cap_required_serv
)))
428 if (cap
->cap_serv
== CLICAP_SASL
)
430 struct Client
*agent_p
= NULL
;
432 if (!ConfigFileEntry
.sasl_service
)
438 agent_p
= find_named_client(ConfigFileEntry
.sasl_service
);
439 if (agent_p
== NULL
|| !IsService(agent_p
))
446 if(cap
->flags
& CLICAP_FLAGS_STICKY
)
448 strcat(pbuf
[i
], "=");
452 capadd
|= cap
->cap_serv
;
457 strcat(pbuf
[i
], "~");
461 strcat(pbuf
[i
], cap
->name
);
462 strcat(pbuf
[i
], " ");
463 plen
+= (cap
->namelen
+ 1);
468 sendto_one(source_p
, ":%s CAP %s NAK :%s",
469 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
, arg
);
475 sendto_one(source_p
, "%s * :%s", buf
, pbuf
[0]);
476 sendto_one(source_p
, "%s :%s", buf
, pbuf
[1]);
479 sendto_one(source_p
, "%s :%s", buf
, pbuf
[0]);
481 source_p
->localClient
->caps
|= capadd
;
482 source_p
->localClient
->caps
&= ~capdel
;
485 static struct clicap_cmd
488 void (*func
)(struct Client
*source_p
, const char *arg
);
489 } clicap_cmdlist
[] = {
490 /* This list *MUST* be in alphabetical order */
492 { "CLEAR", cap_clear
},
494 { "LIST", cap_list
},
500 clicap_cmd_search(const char *command
, struct clicap_cmd
*entry
)
502 return irccmp(command
, entry
->cmd
);
506 m_cap(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
508 struct clicap_cmd
*cmd
;
510 if(!(cmd
= bsearch(parv
[1], clicap_cmdlist
,
511 sizeof(clicap_cmdlist
) / sizeof(struct clicap_cmd
),
512 sizeof(struct clicap_cmd
), (bqcmp
) clicap_cmd_search
)))
514 sendto_one(source_p
, form_str(ERR_INVALIDCAPCMD
),
515 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
,
520 (cmd
->func
)(source_p
, parv
[2]);