X-Git-Url: https://jfr.im/git/solanum.git/blobdiff_plain/ba316ed56cd45b4c1523e06cddccc7789439da6c..83f7888d72cf1faba78a4c9ed9df4780b11534b3:/modules/m_cap.c diff --git a/modules/m_cap.c b/modules/m_cap.c index 1c1a4012..3d6355bf 100644 --- a/modules/m_cap.c +++ b/modules/m_cap.c @@ -2,7 +2,7 @@ * * Copyright (C) 2005 Lee Hardy * Copyright (C) 2005 ircd-ratbox development team - * Copyright (C) 2016 William Pitcock + * 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 @@ -44,9 +44,11 @@ #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 MsgBuf *, struct Client *, struct Client *, int, const char **); +static void m_cap(struct MsgBuf *, struct Client *, struct Client *, int, const char **); struct Message cap_msgtab = { "CAP", 0, 0, 0, 0, @@ -54,16 +56,21 @@ struct Message cap_msgtab = { }; mapi_clist_av1 cap_clist[] = { &cap_msgtab, NULL }; -DECLARE_MODULE_AV1(cap, NULL, NULL, cap_clist, NULL, NULL, "$Revision: 676 $"); + +DECLARE_MODULE_AV2(cap, NULL, NULL, cap_clist, NULL, NULL, NULL, NULL, cap_desc); #define IsCapableEntry(c, e) IsCapable(c, 1 << (e)->value) #define HasCapabilityFlag(c, f) (c->ownerdata != NULL && (((struct ClientCapability *)c->ownerdata)->flags & (f)) == f) static inline int -clicap_visible(const struct CapabilityEntry *cap) +clicap_visible(struct Client *client_p, const struct CapabilityEntry *cap) { struct ClientCapability *clicap; + /* orphaned caps shouldn't be visible */ + if (cap->flags & CAP_ORPHANED) + return 0; + if (cap->ownerdata == NULL) return 1; @@ -71,7 +78,7 @@ clicap_visible(const struct CapabilityEntry *cap) if (clicap->visible == NULL) return 1; - return clicap->visible(); + return clicap->visible(client_p); } /* clicap_find() @@ -139,99 +146,89 @@ 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; + const char *str_cont = "* :"; + const char *str_final = ":"; struct CapabilityEntry *entry; - struct DictionaryIter iter; + rb_dictionary_iter iter; + bool multiline_ret; + enum multiline_item_result multiline_item_ret; - mlen = sprintf(buf, ":%s CAP %s %s", + multiline_ret = send_multiline_init(source_p, " ", ":%s CAP %s %s %s", me.name, EmptyString(source_p->name) ? "*" : source_p->name, - subcmd); - - p = capbuf; - buflen = mlen; + 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; } - DICTIONARY_FOREACH(entry, &iter, cli_capindex->cap_dict) - { - size_t caplen = 0; + 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; - if(flags) + if (pass == 0 && !HasCapabilityFlag(entry, CLICAP_FLAGS_PRIORITY)) + continue; + else if (pass == 1 && HasCapabilityFlag(entry, CLICAP_FLAGS_PRIORITY)) + continue; + + if (!IsCapableEntry(source_p, entry) && ConfigFileEntry.hidden_caps != NULL) { - if(!IsCapableEntry(source_p, entry)) - continue; - /* they are capable of this, check sticky */ - else if(clear && HasCapabilityFlag(entry, CLICAP_FLAGS_STICKY)) + size_t i; + for (i = 0; ConfigFileEntry.hidden_caps[i] != NULL; i++) + { + if (!rb_strcasecmp(entry->cap, ConfigFileEntry.hidden_caps[i])) + break; + } + if (ConfigFileEntry.hidden_caps[i] != NULL) continue; } - if (!clicap_visible(entry)) + if (flags && !IsCapableEntry(source_p, entry)) continue; - caplen = strlen(entry->cap); - if (!flags && (source_p->flags & FLAGS_CLICAP_DATA) && clicap != NULL && clicap->data != NULL) - data = clicap->data(); - - if (data != NULL) - caplen += strlen(data) + 1; + if (!clicap_visible(source_p, entry)) + continue; - /* \r\n\0, possible "-~=", space, " *" */ - if(buflen + caplen >= 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 (source_p->flags & FLAGS_CLICAP_DATA) { + if (!flags && clicap != NULL && clicap->data != NULL) + data = clicap->data(source_p); - if(clear) - { - *p++ = '-'; - buflen++; - } + multiline_item_ret = send_multiline_item(source_p, "%s%s%s", + entry->cap, + data != NULL ? "=" : "", + data != NULL ? data : ""); - if (data == NULL) - curlen = sprintf(p, "%s ", entry->cap); - else - curlen = sprintf(p, "%s=%s ", entry->cap, data); + if (multiline_item_ret == MULTILINE_FAILURE) + return; + } else { + multiline_item_ret = send_multiline_item(source_p, "%s", entry->cap); - p += curlen; - buflen += curlen; + if (multiline_item_ret != MULTILINE_SUCCESS) { + send_multiline_reset(); + return; + } + } } - /* 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 @@ -267,15 +264,6 @@ cap_ack(struct Client *source_p, const char *arg) 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); - - source_p->localClient->caps = 0; -} - static void cap_end(struct Client *source_p, const char *arg) { @@ -295,35 +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 (arg != NULL && !strcmp(arg, "302")) - { + 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]; + char ack_buf[DATALEN+1]; struct CapabilityEntry *cap; - int buflen, plen; - int i = 0; 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; @@ -331,28 +323,19 @@ cap_req(struct Client *source_p, const char *arg) if(EmptyString(arg)) return; - buflen = 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)) { - size_t namelen = strlen(cap->cap); - - /* 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 + namelen + 6 >= BUFSIZE) - { - pbuf[1][0] = '\0'; - plen = 0; - i = 1; - } - if(negate) { if(HasCapabilityFlag(cap, CLICAP_FLAGS_STICKY)) @@ -361,14 +344,11 @@ cap_req(struct Client *source_p, const char *arg) break; } - strcat(pbuf[i], "-"); - plen++; - capdel |= (1 << cap->value); } else { - if (!clicap_visible(cap)) + if (!clicap_visible(source_p, cap)) { finished = 0; break; @@ -377,18 +357,6 @@ cap_req(struct Client *source_p, const char *arg) capadd |= (1 << cap->value); } - /* XXX this probably should exclude REQACK'd caps from capadd/capdel, but keep old behaviour for now */ - if(HasCapabilityFlag(cap, CLICAP_FLAGS_REQACK)) - { - strcat(pbuf[i], "~"); - plen++; - } - - strcat(pbuf[i], cap->cap); - if (!finished) { - strcat(pbuf[i], " "); - plen += (namelen + 1); - } } if(!finished) @@ -398,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 @@ -417,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 }, @@ -430,7 +398,7 @@ clicap_cmd_search(const char *command, struct clicap_cmd *entry) return irccmp(command, entry->cmd); } -static int +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; @@ -442,9 +410,8 @@ m_cap(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, 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; }