cap: substantial rewrite leveraging the ircd capabilities framework for client caps

This commit is contained in:
William Pitcock 2016-02-27 01:41:36 -06:00
parent ba83226733
commit 32df5e96a6
7 changed files with 101 additions and 102 deletions

View file

@ -38,6 +38,7 @@ struct CapabilityEntry {
void *ownerdata; void *ownerdata;
}; };
extern struct CapabilityEntry *capability_find(struct CapabilityIndex *idx, const char *cap);
extern unsigned int capability_get(struct CapabilityIndex *idx, const char *cap, void **ownerdata); extern unsigned int capability_get(struct CapabilityIndex *idx, const char *cap, void **ownerdata);
extern unsigned int capability_put(struct CapabilityIndex *idx, const char *cap, void *ownerdata); extern unsigned int capability_put(struct CapabilityIndex *idx, const char *cap, void *ownerdata);
extern unsigned int capability_put_anonymous(struct CapabilityIndex *idx); extern unsigned int capability_put_anonymous(struct CapabilityIndex *idx);

View file

@ -445,18 +445,6 @@ struct ListClient
UMODE_WALLOP | UMODE_LOCOPS) UMODE_WALLOP | UMODE_LOCOPS)
#define DEFAULT_OPER_SNOMASK SNO_GENERAL #define DEFAULT_OPER_SNOMASK SNO_GENERAL
/* XXX make clicap a registry */
#define CLICAP_MULTI_PREFIX 0x0001
#define CLICAP_SASL 0x0002
#define CLICAP_ACCOUNT_NOTIFY 0x0004
#define CLICAP_EXTENDED_JOIN 0x0008
#define CLICAP_AWAY_NOTIFY 0x0010
#define CLICAP_TLS 0x0020
#define CLICAP_USERHOST_IN_NAMES 0x0040
#define CLICAP_CAP_NOTIFY 0x0080
#define CLICAP_CHGHOST 0x0100
#define CLICAP_ACCOUNT_TAG 0x0200
/* /*
* flags macros. * flags macros.
*/ */

View file

@ -51,6 +51,28 @@ struct Channel;
extern struct CapabilityIndex *serv_capindex; extern struct CapabilityIndex *serv_capindex;
extern struct CapabilityIndex *cli_capindex; extern struct CapabilityIndex *cli_capindex;
/* register client capabilities with this structure for 3.2 enhanced capability negotiation */
#define CLICAP_FLAGS_STICKY 0x001
#define CLICAP_FLAGS_REQACK 0x002
struct ClientCapability {
int (*visible)(void); /* whether or not to display the capability. set to NULL or true return value = displayed */
const char *(*data)(void); /* any custom data for the capability. set to NULL or return NULL = no data */
unsigned int flags;
};
/* builtin client capabilities */
extern unsigned int CLICAP_MULTI_PREFIX;
extern unsigned int CLICAP_SASL;
extern unsigned int CLICAP_ACCOUNT_NOTIFY;
extern unsigned int CLICAP_EXTENDED_JOIN;
extern unsigned int CLICAP_AWAY_NOTIFY;
extern unsigned int CLICAP_TLS;
extern unsigned int CLICAP_USERHOST_IN_NAMES;
extern unsigned int CLICAP_CAP_NOTIFY;
extern unsigned int CLICAP_CHGHOST;
extern unsigned int CLICAP_ACCOUNT_TAG;
/* /*
* XXX: this is kind of ugly, but this allows us to have backwards * XXX: this is kind of ugly, but this allows us to have backwards
* API-compatibility. * API-compatibility.

View file

@ -25,6 +25,16 @@
static rb_dlink_list capability_indexes = { NULL, NULL, 0 }; static rb_dlink_list capability_indexes = { NULL, NULL, 0 };
struct CapabilityEntry *
capability_find(struct CapabilityIndex *idx, const char *cap)
{
s_assert(idx != NULL);
if (cap == NULL)
return NULL;
return irc_dictionary_retrieve(idx->cap_dict, cap);
}
unsigned int unsigned int
capability_get(struct CapabilityIndex *idx, const char *cap, void **ownerdata) capability_get(struct CapabilityIndex *idx, const char *cap, void **ownerdata)
{ {

View file

@ -95,6 +95,16 @@ unsigned int CAP_EOPMOD;
unsigned int CAP_BAN; unsigned int CAP_BAN;
unsigned int CAP_MLOCK; unsigned int CAP_MLOCK;
unsigned int CLICAP_MULTI_PREFIX;
unsigned int CLICAP_SASL;
unsigned int CLICAP_ACCOUNT_NOTIFY;
unsigned int CLICAP_EXTENDED_JOIN;
unsigned int CLICAP_AWAY_NOTIFY;
unsigned int CLICAP_TLS;
unsigned int CLICAP_USERHOST_IN_NAMES;
unsigned int CLICAP_CAP_NOTIFY;
unsigned int CLICAP_CHGHOST;
/* /*
* initialize our builtin capability table. --nenolod * initialize our builtin capability table. --nenolod
*/ */
@ -132,6 +142,16 @@ init_builtin_capabs(void)
capability_require(serv_capindex, "ENCAP"); capability_require(serv_capindex, "ENCAP");
cli_capindex = capability_index_create("client capabilities"); cli_capindex = capability_index_create("client capabilities");
CLICAP_MULTI_PREFIX = capability_put(cli_capindex, "multi-prefix", NULL);
CLICAP_SASL = capability_put(cli_capindex, "sasl", NULL);
CLICAP_ACCOUNT_NOTIFY = capability_put(cli_capindex, "account-notify", NULL);
CLICAP_EXTENDED_JOIN = capability_put(cli_capindex, "extended-join", NULL);
CLICAP_AWAY_NOTIFY = capability_put(cli_capindex, "away-notify", NULL);
CLICAP_TLS = capability_put(cli_capindex, "tls", NULL);
CLICAP_USERHOST_IN_NAMES = capability_put(cli_capindex, "userhost-in-names", NULL);
CLICAP_CAP_NOTIFY = capability_put(cli_capindex, "cap-notify", NULL);
CLICAP_CHGHOST = capability_put(cli_capindex, "chghost", NULL);
} }
static CNCB serv_connect_callback; static CNCB serv_connect_callback;

View file

@ -41,6 +41,8 @@ mapi_hfn_list_av1 cap_account_tag_hfnlist[] = {
{ NULL, NULL } { NULL, NULL }
}; };
unsigned int CLICAP_ACCOUNT_TAG = 0;
static void static void
cap_account_tag_process(hook_data *data) cap_account_tag_process(hook_data *data)
{ {
@ -50,4 +52,17 @@ cap_account_tag_process(hook_data *data)
msgbuf_append_tag(msgbuf, "account", data->client->user->suser, CLICAP_ACCOUNT_TAG); msgbuf_append_tag(msgbuf, "account", data->client->user->suser, CLICAP_ACCOUNT_TAG);
} }
DECLARE_MODULE_AV1(cap_account_tag, NULL, NULL, NULL, NULL, cap_account_tag_hfnlist, "$Revision$"); static int
_modinit(void)
{
CLICAP_ACCOUNT_TAG = capability_put(cli_capindex, "account-tag", NULL);
return 0;
}
static void
_moddeinit(void)
{
capability_orphan(cli_capindex, "account-tag");
}
DECLARE_MODULE_AV1(cap_account_tag, _modinit, _moddeinit, NULL, NULL, cap_account_tag_hfnlist, "$Revision$");

View file

@ -2,6 +2,7 @@
* *
* Copyright (C) 2005 Lee Hardy <lee@leeh.co.uk> * Copyright (C) 2005 Lee Hardy <lee@leeh.co.uk>
* Copyright (C) 2005 ircd-ratbox development team * Copyright (C) 2005 ircd-ratbox development team
* Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -26,8 +27,6 @@
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*
* $Id: m_cap.c 676 2006-02-03 20:05:09Z gxti $
*/ */
#include "stdinc.h" #include "stdinc.h"
@ -48,7 +47,6 @@
typedef int (*bqcmp)(const void *, const void *); typedef int (*bqcmp)(const void *, const void *);
static int m_cap(struct MsgBuf *, struct Client *, struct Client *, int, const char **); static int m_cap(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
static int modinit(void);
struct Message cap_msgtab = { struct Message cap_msgtab = {
"CAP", 0, 0, 0, 0, "CAP", 0, 0, 0, 0,
@ -56,57 +54,10 @@ struct Message cap_msgtab = {
}; };
mapi_clist_av1 cap_clist[] = { &cap_msgtab, NULL }; mapi_clist_av1 cap_clist[] = { &cap_msgtab, NULL };
DECLARE_MODULE_AV1(cap, modinit, NULL, cap_clist, NULL, NULL, "$Revision: 676 $"); DECLARE_MODULE_AV1(cap, NULL, NULL, cap_clist, NULL, NULL, "$Revision: 676 $");
#define _CLICAP(name, capserv, capclient, caprequired, flags) \ #define IsCapableEntry(c, e) IsCapable(c, 1 << (e)->value)
{ (name), (capserv), (capclient), (caprequired), (flags), sizeof(name) - 1 } #define HasCapabilityFlag(c, f) (c->ownerdata != NULL && (((struct ClientCapability *)c->ownerdata)->flags & (f)) == f)
#define CLICAP_FLAGS_STICKY 0x001
static struct clicap
{
const char *name;
int cap_serv; /* for altering s->c */
int cap_cli; /* for altering c->s */
int cap_required_serv; /* required dependency cap */
int flags;
int namelen;
} clicap_list[] = {
_CLICAP("multi-prefix", CLICAP_MULTI_PREFIX, 0, 0, 0),
_CLICAP("sasl", CLICAP_SASL, 0, 0, CLICAP_FLAGS_STICKY),
_CLICAP("account-notify", CLICAP_ACCOUNT_NOTIFY, 0, 0, 0),
_CLICAP("extended-join", CLICAP_EXTENDED_JOIN, 0, 0, 0),
_CLICAP("away-notify", CLICAP_AWAY_NOTIFY, 0, 0, 0),
_CLICAP("tls", CLICAP_TLS, 0, 0, 0),
_CLICAP("userhost-in-names", CLICAP_USERHOST_IN_NAMES, 0, 0, 0),
_CLICAP("cap-notify", CLICAP_CAP_NOTIFY, 0, 0, 0),
_CLICAP("chghost", CLICAP_CHGHOST, 0, 0, 0),
_CLICAP("account-tag", CLICAP_ACCOUNT_TAG, 0, 0, 0),
};
#define CLICAP_LIST_LEN (sizeof(clicap_list) / sizeof(struct clicap))
static int clicap_sort(struct clicap *, struct clicap *);
static int
modinit(void)
{
qsort(clicap_list, CLICAP_LIST_LEN, sizeof(struct clicap),
(bqcmp) clicap_sort);
return 0;
}
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);
}
/* clicap_find() /* clicap_find()
* Used iteratively over a buffer, extracts individual cap tokens. * Used iteratively over a buffer, extracts individual cap tokens.
@ -116,12 +67,12 @@ clicap_compare(const char *name, struct clicap *cap)
* int pointer to whether we finish with success * int pointer to whether we finish with success
* Ouputs: Cap entry if found, NULL otherwise. * Ouputs: Cap entry if found, NULL otherwise.
*/ */
static struct clicap * static struct CapabilityEntry *
clicap_find(const char *data, int *negate, int *finished) clicap_find(const char *data, int *negate, int *finished)
{ {
static char buf[BUFSIZE]; static char buf[BUFSIZE];
static char *p; static char *p;
struct clicap *cap; struct CapabilityEntry *cap;
char *s; char *s;
*negate = 0; *negate = 0;
@ -158,8 +109,7 @@ clicap_find(const char *data, int *negate, int *finished)
if((s = strchr(p, ' '))) if((s = strchr(p, ' ')))
*s++ = '\0'; *s++ = '\0';
if((cap = bsearch(p, clicap_list, CLICAP_LIST_LEN, if((cap = capability_find(cli_capindex, p)) != NULL)
sizeof(struct clicap), (bqcmp) clicap_compare)))
{ {
if(s) if(s)
p = s; p = s;
@ -186,7 +136,8 @@ clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clea
char *p; char *p;
int buflen = 0; int buflen = 0;
int curlen, mlen; int curlen, mlen;
size_t i; struct CapabilityEntry *entry;
struct DictionaryIter iter;
mlen = sprintf(buf, ":%s CAP %s %s", mlen = sprintf(buf, ":%s CAP %s %s",
me.name, me.name,
@ -203,18 +154,18 @@ clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clea
return; return;
} }
for(i = 0; i < CLICAP_LIST_LEN; i++) DICTIONARY_FOREACH(entry, &iter, cli_capindex->cap_dict)
{ {
if(flags) if(flags)
{ {
if(!IsCapable(source_p, clicap_list[i].cap_serv)) if(!IsCapableEntry(source_p, entry))
continue; continue;
/* they are capable of this, check sticky */ /* they are capable of this, check sticky */
else if(clear && clicap_list[i].flags & CLICAP_FLAGS_STICKY) else if(clear && HasCapabilityFlag(entry, CLICAP_FLAGS_STICKY))
continue; continue;
} }
if (clicap_list[i].cap_serv == CLICAP_SASL) if ((1 << entry->value) == CLICAP_SASL)
{ {
struct Client *agent_p = NULL; struct Client *agent_p = NULL;
@ -227,7 +178,7 @@ clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clea
} }
/* \r\n\0, possible "-~=", space, " *" */ /* \r\n\0, possible "-~=", space, " *" */
if(buflen + clicap_list[i].namelen >= BUFSIZE - 10) if(buflen + strlen(entry->cap) >= BUFSIZE - 10)
{ {
/* remove our trailing space -- if buflen == mlen /* remove our trailing space -- if buflen == mlen
* here, we didnt even succeed in adding one. * here, we didnt even succeed in adding one.
@ -248,7 +199,7 @@ clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clea
buflen++; buflen++;
} }
curlen = sprintf(p, "%s ", clicap_list[i].name); curlen = sprintf(p, "%s ", entry->cap);
p += curlen; p += curlen;
buflen += curlen; buflen += curlen;
} }
@ -265,7 +216,7 @@ clicap_generate(struct Client *source_p, const char *subcmd, int flags, int clea
static void static void
cap_ack(struct Client *source_p, const char *arg) cap_ack(struct Client *source_p, const char *arg)
{ {
struct clicap *cap; struct CapabilityEntry *cap;
int capadd = 0, capdel = 0; int capadd = 0, capdel = 0;
int finished = 0, negate; int finished = 0, negate;
@ -276,19 +227,19 @@ cap_ack(struct Client *source_p, const char *arg)
cap = clicap_find(NULL, &negate, &finished)) cap = clicap_find(NULL, &negate, &finished))
{ {
/* sent an ACK for something they havent REQd */ /* sent an ACK for something they havent REQd */
if(!IsCapable(source_p, cap->cap_serv)) if(!IsCapableEntry(source_p, cap))
continue; continue;
if(negate) if(negate)
{ {
/* dont let them ack something sticky off */ /* dont let them ack something sticky off */
if(cap->flags & CLICAP_FLAGS_STICKY) if(HasCapabilityFlag(cap, CLICAP_FLAGS_STICKY))
continue; continue;
capdel |= cap->cap_cli; capdel |= (1 << cap->value);
} }
else else
capadd |= cap->cap_cli; capadd |= (1 << cap->value);
} }
source_p->localClient->caps |= capadd; source_p->localClient->caps |= capadd;
@ -301,12 +252,7 @@ cap_clear(struct Client *source_p, const char *arg)
clicap_generate(source_p, "ACK", clicap_generate(source_p, "ACK",
source_p->localClient->caps ? source_p->localClient->caps : -1, 1); 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; source_p->localClient->caps = 0;
#endif
} }
static void static void
@ -346,7 +292,7 @@ cap_req(struct Client *source_p, const char *arg)
{ {
char buf[BUFSIZE]; char buf[BUFSIZE];
char pbuf[2][BUFSIZE]; char pbuf[2][BUFSIZE];
struct clicap *cap; struct CapabilityEntry *cap;
int buflen, plen; int buflen, plen;
int i = 0; int i = 0;
int capadd = 0, capdel = 0; int capadd = 0, capdel = 0;
@ -367,11 +313,13 @@ cap_req(struct Client *source_p, const char *arg)
for(cap = clicap_find(arg, &negate, &finished); cap; for(cap = clicap_find(arg, &negate, &finished); cap;
cap = clicap_find(NULL, &negate, &finished)) cap = clicap_find(NULL, &negate, &finished))
{ {
size_t namelen = strlen(cap->cap);
/* filled the first array, but cant send it in case the /* filled the first array, but cant send it in case the
* request fails. one REQ should never fill more than two * request fails. one REQ should never fill more than two
* buffers --fl * buffers --fl
*/ */
if(buflen + plen + cap->namelen + 6 >= BUFSIZE) if(buflen + plen + namelen + 6 >= BUFSIZE)
{ {
pbuf[1][0] = '\0'; pbuf[1][0] = '\0';
plen = 0; plen = 0;
@ -380,7 +328,7 @@ cap_req(struct Client *source_p, const char *arg)
if(negate) if(negate)
{ {
if(cap->flags & CLICAP_FLAGS_STICKY) if(HasCapabilityFlag(cap, CLICAP_FLAGS_STICKY))
{ {
finished = 0; finished = 0;
break; break;
@ -389,17 +337,11 @@ cap_req(struct Client *source_p, const char *arg)
strcat(pbuf[i], "-"); strcat(pbuf[i], "-");
plen++; plen++;
capdel |= cap->cap_serv; capdel |= (1 << cap->value);
} }
else else
{ {
if(cap->cap_required_serv && !((capadd & cap->cap_required_serv) == cap->cap_required_serv || IsCapable(source_p, cap->cap_required_serv))) if ((1 << cap->value) == CLICAP_SASL)
{
finished = 0;
break;
}
if (cap->cap_serv == CLICAP_SASL)
{ {
struct Client *agent_p = NULL; struct Client *agent_p = NULL;
@ -417,19 +359,20 @@ cap_req(struct Client *source_p, const char *arg)
} }
} }
capadd |= cap->cap_serv; capadd |= (1 << cap->value);
} }
if(cap->cap_cli) /* 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], "~"); strcat(pbuf[i], "~");
plen++; plen++;
} }
strcat(pbuf[i], cap->name); strcat(pbuf[i], cap->cap);
if (!finished) { if (!finished) {
strcat(pbuf[i], " "); strcat(pbuf[i], " ");
plen += (cap->namelen + 1); plen += (namelen + 1);
} }
} }