X-Git-Url: https://jfr.im/git/solanum.git/blobdiff_plain/c239b97f68e49bc7015af4ca274d7bbef266add0..1548c140218b956485b3fd5c386447a2add59864:/modules/m_cap.c diff --git a/modules/m_cap.c b/modules/m_cap.c index c5283a8c..3d6355bf 100644 --- a/modules/m_cap.c +++ b/modules/m_cap.c @@ -1,7 +1,8 @@ /* modules/m_cap.c - * + * * Copyright (C) 2005 Lee Hardy * Copyright (C) 2005 ircd-ratbox development team + * Copyright (C) 2016 Ariadne Conill * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -26,8 +27,6 @@ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: m_cap.c 676 2006-02-03 20:05:09Z gxti $ */ #include "stdinc.h" @@ -41,60 +40,45 @@ #include "modules.h" #include "s_serv.h" #include "s_user.h" +#include "send.h" +#include "s_conf.h" +#include "hash.h" + +static const char cap_desc[] = "Provides the commands used for client capability negotiation"; typedef int (*bqcmp)(const void *, const void *); -static int m_cap(struct Client *, struct Client *, int, const char **); -static int modinit(void); +static void m_cap(struct MsgBuf *, struct Client *, struct Client *, int, const char **); struct Message cap_msgtab = { - "CAP", 0, 0, 0, MFLG_SLOW, + "CAP", 0, 0, 0, 0, {{m_cap, 2}, {m_cap, 2}, mg_ignore, mg_ignore, mg_ignore, {m_cap, 2}} }; mapi_clist_av1 cap_clist[] = { &cap_msgtab, NULL }; -DECLARE_MODULE_AV1(cap, modinit, NULL, cap_clist, NULL, NULL, "$Revision: 676 $"); -#define _CLICAP(name, capserv, capclient, flags) \ - { (name), (capserv), (capclient), (flags), sizeof(name) - 1 } +DECLARE_MODULE_AV2(cap, NULL, NULL, cap_clist, NULL, NULL, NULL, NULL, cap_desc); -#define CLICAP_FLAGS_STICKY 0x001 +#define IsCapableEntry(c, e) IsCapable(c, 1 << (e)->value) +#define HasCapabilityFlag(c, f) (c->ownerdata != NULL && (((struct ClientCapability *)c->ownerdata)->flags & (f)) == f) -static struct clicap +static inline int +clicap_visible(struct Client *client_p, const struct CapabilityEntry *cap) { - const char *name; - int cap_serv; /* for altering s->c */ - int cap_cli; /* for altering c->s */ - int flags; - int namelen; -} clicap_list[] = { - _CLICAP("multi-prefix", CLICAP_MULTI_PREFIX, 0, 0), - _CLICAP("sasl", CLICAP_SASL, 0, 0), - _CLICAP("source-account-hostmask", CLICAP_ACCOUNT_HOSTMASK, 0, 0), -}; + struct ClientCapability *clicap; -#define CLICAP_LIST_LEN (sizeof(clicap_list) / sizeof(struct clicap)) + /* orphaned caps shouldn't be visible */ + if (cap->flags & CAP_ORPHANED) + return 0; -static int clicap_sort(struct clicap *, struct clicap *); + if (cap->ownerdata == NULL) + return 1; -static int -modinit(void) -{ - qsort(clicap_list, CLICAP_LIST_LEN, sizeof(struct clicap), - (bqcmp) clicap_sort); - return 0; -} + clicap = cap->ownerdata; + if (clicap->visible == NULL) + return 1; -static int -clicap_sort(struct clicap *one, struct clicap *two) -{ - return irccmp(one->name, two->name); -} - -static int -clicap_compare(const char *name, struct clicap *cap) -{ - return irccmp(name, cap->name); + return clicap->visible(client_p); } /* clicap_find() @@ -105,12 +89,12 @@ clicap_compare(const char *name, struct clicap *cap) * int pointer to whether we finish with success * Ouputs: Cap entry if found, NULL otherwise. */ -static struct clicap * +static struct CapabilityEntry * clicap_find(const char *data, int *negate, int *finished) { static char buf[BUFSIZE]; static char *p; - struct clicap *cap; + struct CapabilityEntry *cap; char *s; *negate = 0; @@ -147,8 +131,7 @@ clicap_find(const char *data, int *negate, int *finished) if((s = strchr(p, ' '))) *s++ = '\0'; - if((cap = bsearch(p, clicap_list, CLICAP_LIST_LEN, - sizeof(struct clicap), (bqcmp) clicap_compare))) + if((cap = capability_find(cli_capindex, p)) != NULL) { if(s) p = s; @@ -163,112 +146,95 @@ clicap_find(const char *data, int *negate, int *finished) * Generates a list of capabilities. * * Inputs: client to send to, subcmd to send, - * flags to match against: 0 to do none, -1 if client has no flags, - * int to whether we are doing CAP CLEAR + * flags to match against: 0 to do none, -1 if client has no flags * Outputs: None */ static void -clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clear) +clicap_generate(struct Client *source_p, const char *subcmd, int flags) { - char buf[BUFSIZE]; - char capbuf[BUFSIZE]; - char *p; - int buflen = 0; - int curlen, mlen; - int i; - - mlen = rb_sprintf(buf, ":%s CAP %s %s", - me.name, - EmptyString(source_p->name) ? "*" : source_p->name, - subcmd); - - p = capbuf; - buflen = mlen; + const char *str_cont = "* :"; + const char *str_final = ":"; + struct CapabilityEntry *entry; + rb_dictionary_iter iter; + bool multiline_ret; + enum multiline_item_result multiline_item_ret; + + multiline_ret = send_multiline_init(source_p, " ", ":%s CAP %s %s %s", + me.name, + EmptyString(source_p->name) ? "*" : source_p->name, + subcmd, + (source_p->flags & FLAGS_CLICAP_DATA) ? str_cont : str_final); /* shortcut, nothing to do */ - if(flags == -1) - { - sendto_one(source_p, "%s :", buf); + if (flags == -1 || !multiline_ret) { + sendto_one(source_p, ":%s CAP %s %s %s", + me.name, + EmptyString(source_p->name) ? "*" : source_p->name, + subcmd, + str_final); return; } - for(i = 0; i < CLICAP_LIST_LEN; i++) - { - if(flags) - { - if(!IsCapable(source_p, clicap_list[i].cap_serv)) - continue; - /* they are capable of this, check sticky */ - else if(clear && clicap_list[i].flags & CLICAP_FLAGS_STICKY) - continue; - } + for (int pass = 0; pass < 2; pass++) + RB_DICTIONARY_FOREACH(entry, &iter, cli_capindex->cap_dict) { + struct ClientCapability *clicap = entry->ownerdata; + const char *data = NULL; - /* \r\n\0, possible "-~=", space, " *" */ - if(buflen + clicap_list[i].namelen >= BUFSIZE - 10) - { - /* remove our trailing space -- if buflen == mlen - * here, we didnt even succeed in adding one. - */ - if(buflen != mlen) - *(p - 1) = '\0'; - else - *p = '\0'; - - sendto_one(source_p, "%s * :%s", buf, capbuf); - p = capbuf; - buflen = mlen; - } + if (pass == 0 && !HasCapabilityFlag(entry, CLICAP_FLAGS_PRIORITY)) + continue; + else if (pass == 1 && HasCapabilityFlag(entry, CLICAP_FLAGS_PRIORITY)) + continue; - if(clear) + if (!IsCapableEntry(source_p, entry) && ConfigFileEntry.hidden_caps != NULL) { - *p++ = '-'; - buflen++; - - /* needs a client ack */ - if(clicap_list[i].cap_cli && - IsCapable(source_p, clicap_list[i].cap_cli)) + size_t i; + for (i = 0; ConfigFileEntry.hidden_caps[i] != NULL; i++) { - *p++ = '~'; - buflen++; + if (!rb_strcasecmp(entry->cap, ConfigFileEntry.hidden_caps[i])) + break; } + if (ConfigFileEntry.hidden_caps[i] != NULL) + continue; } - else - { - if(clicap_list[i].flags & CLICAP_FLAGS_STICKY) - { - *p++ = '='; - buflen++; - } - /* if we're doing an LS, then we only send this if - * they havent ack'd - */ - if(clicap_list[i].cap_cli && - (!flags || !IsCapable(source_p, clicap_list[i].cap_cli))) - { - *p++ = '~'; - buflen++; + if (flags && !IsCapableEntry(source_p, entry)) + continue; + + if (!clicap_visible(source_p, entry)) + continue; + + if (source_p->flags & FLAGS_CLICAP_DATA) { + if (!flags && clicap != NULL && clicap->data != NULL) + data = clicap->data(source_p); + + multiline_item_ret = send_multiline_item(source_p, "%s%s%s", + entry->cap, + data != NULL ? "=" : "", + data != NULL ? data : ""); + + if (multiline_item_ret == MULTILINE_FAILURE) + return; + } else { + multiline_item_ret = send_multiline_item(source_p, "%s", entry->cap); + + if (multiline_item_ret != MULTILINE_SUCCESS) { + send_multiline_reset(); + return; } } - - curlen = rb_sprintf(p, "%s ", clicap_list[i].name); - p += curlen; - buflen += curlen; } - /* remove trailing space */ - if(buflen != mlen) - *(p - 1) = '\0'; - else - *p = '\0'; - - sendto_one(source_p, "%s :%s", buf, capbuf); + send_multiline_fini(source_p, ":%s CAP %s %s %s", + me.name, + EmptyString(source_p->name) ? "*" : source_p->name, + subcmd, + str_final); } static void cap_ack(struct Client *source_p, const char *arg) { - struct clicap *cap; + struct CapabilityEntry *cap; int capadd = 0, capdel = 0; int finished = 0, negate; @@ -279,39 +245,25 @@ cap_ack(struct Client *source_p, const char *arg) cap = clicap_find(NULL, &negate, &finished)) { /* sent an ACK for something they havent REQd */ - if(!IsCapable(source_p, cap->cap_serv)) + if(!IsCapableEntry(source_p, cap)) continue; if(negate) { /* dont let them ack something sticky off */ - if(cap->flags & CLICAP_FLAGS_STICKY) + if(HasCapabilityFlag(cap, CLICAP_FLAGS_STICKY)) continue; - capdel |= cap->cap_cli; + capdel |= (1 << cap->value); } else - capadd |= cap->cap_cli; + capadd |= (1 << cap->value); } source_p->localClient->caps |= capadd; source_p->localClient->caps &= ~capdel; } -static void -cap_clear(struct Client *source_p, const char *arg) -{ - clicap_generate(source_p, "ACK", - source_p->localClient->caps ? source_p->localClient->caps : -1, 1); - - /* XXX - sticky capabs */ -#ifdef CLICAP_STICKY - source_p->localClient->caps = source_p->localClient->caps & CLICAP_STICKY; -#else - source_p->localClient->caps = 0; -#endif -} - static void cap_end(struct Client *source_p, const char *arg) { @@ -322,9 +274,7 @@ cap_end(struct Client *source_p, const char *arg) if(source_p->name[0] && source_p->flags & FLAGS_SENTUSER) { - char buf[USERLEN+1]; - rb_strlcpy(buf, source_p->username, sizeof(buf)); - register_local_user(source_p, source_p, buf); + register_local_user(source_p, source_p); } } @@ -333,29 +283,39 @@ cap_list(struct Client *source_p, const char *arg) { /* list of what theyre currently using */ clicap_generate(source_p, "LIST", - source_p->localClient->caps ? source_p->localClient->caps : -1, 0); + source_p->localClient->caps ? source_p->localClient->caps : -1); } static void cap_ls(struct Client *source_p, const char *arg) { + int caps_version = 301; + if(!IsRegistered(source_p)) source_p->flags |= FLAGS_CLICAP; + if (!EmptyString(arg)) { + caps_version = atoi(arg); + } + + if (caps_version >= 302) { + source_p->flags |= FLAGS_CLICAP_DATA; + source_p->localClient->caps |= CLICAP_CAP_NOTIFY; + } + /* list of what we support */ - clicap_generate(source_p, "LS", 0, 0); + clicap_generate(source_p, "LS", 0); } static void cap_req(struct Client *source_p, const char *arg) { - char buf[BUFSIZE]; - char pbuf[2][BUFSIZE]; - struct clicap *cap; - int buflen, plen; - int i = 0; + char ack_buf[DATALEN+1]; + struct CapabilityEntry *cap; int capadd = 0, capdel = 0; int finished = 0, negate; + int ret; + hook_data_cap_change hdata; if(!IsRegistered(source_p)) source_p->flags |= FLAGS_CLICAP; @@ -363,59 +323,40 @@ cap_req(struct Client *source_p, const char *arg) if(EmptyString(arg)) return; - buflen = rb_snprintf(buf, sizeof(buf), ":%s CAP %s ACK", - me.name, EmptyString(source_p->name) ? "*" : source_p->name); + ret = snprintf(ack_buf, sizeof ack_buf, ":%s CAP %s ACK :%s", + me.name, EmptyString(source_p->name)? "*" : source_p->name, arg); - pbuf[0][0] = '\0'; - plen = 0; + if (ret < 0 || ret > DATALEN) + { + sendto_one(source_p, ":%s CAP %s NAK :%s", + me.name, EmptyString(source_p->name) ? "*" : source_p->name, arg); + return; + } for(cap = clicap_find(arg, &negate, &finished); cap; cap = clicap_find(NULL, &negate, &finished)) { - /* filled the first array, but cant send it in case the - * request fails. one REQ should never fill more than two - * buffers --fl - */ - if(buflen + plen + cap->namelen + 6 >= BUFSIZE) - { - pbuf[1][0] = '\0'; - plen = 0; - i = 1; - } - if(negate) { - if(cap->flags & CLICAP_FLAGS_STICKY) + if(HasCapabilityFlag(cap, CLICAP_FLAGS_STICKY)) { finished = 0; break; } - strcat(pbuf[i], "-"); - plen++; - - capdel |= cap->cap_serv; + capdel |= (1 << cap->value); } else { - if(cap->flags & CLICAP_FLAGS_STICKY) + if (!clicap_visible(source_p, cap)) { - strcat(pbuf[i], "="); - plen++; + finished = 0; + break; } - capadd |= cap->cap_serv; - } - - if(cap->cap_cli) - { - strcat(pbuf[i], "~"); - plen++; + capadd |= (1 << cap->value); } - strcat(pbuf[i], cap->name); - strcat(pbuf[i], " "); - plen += (cap->namelen + 1); } if(!finished) @@ -425,16 +366,17 @@ cap_req(struct Client *source_p, const char *arg) return; } - if(i) - { - sendto_one(source_p, "%s * :%s", buf, pbuf[0]); - sendto_one(source_p, "%s :%s", buf, pbuf[1]); - } - else - sendto_one(source_p, "%s :%s", buf, pbuf[0]); + sendto_one(source_p, "%s", ack_buf); + + hdata.client = source_p; + hdata.oldcaps = source_p->localClient->caps; + hdata.add = capadd; + hdata.del = capdel; source_p->localClient->caps |= capadd; source_p->localClient->caps &= ~capdel; + + call_hook(h_cap_change, &hdata); } static struct clicap_cmd @@ -444,7 +386,6 @@ static struct clicap_cmd } clicap_cmdlist[] = { /* This list *MUST* be in alphabetical order */ { "ACK", cap_ack }, - { "CLEAR", cap_clear }, { "END", cap_end }, { "LIST", cap_list }, { "LS", cap_ls }, @@ -457,8 +398,8 @@ clicap_cmd_search(const char *command, struct clicap_cmd *entry) return irccmp(command, entry->cmd); } -static int -m_cap(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +static void +m_cap(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { struct clicap_cmd *cmd; @@ -469,9 +410,8 @@ m_cap(struct Client *client_p, struct Client *source_p, int parc, const char *pa sendto_one(source_p, form_str(ERR_INVALIDCAPCMD), me.name, EmptyString(source_p->name) ? "*" : source_p->name, parv[1]); - return 0; + return; } (cmd->func)(source_p, parv[2]); - return 0; }