From 8aabb973c04d3bf2b9bf121544dc47e20b9d7d4e Mon Sep 17 00:00:00 2001 From: Elizabeth Jennifer Myers Date: Wed, 6 Jul 2011 13:35:11 -0400 Subject: [PATCH] Implement chanroles, as discussed with nenolod. The theory behind this is that services sends an ENCAP * GRANT #channel UID :+flagspec message specifying the chanroles the user has. They are mapped into flag bits and applied to the membership of the user. They then are restricted or permitted to what they can do based on the permissions mask regardless of rank. For backwards compatibility, the default permission bit (without a GRANT statement) allows a user to to anything an existing op can do ONLY if they are an op. Todo: make CHANROLE_STATUS work (the ability to apply +ov to people), which is at the moment controlled by CHANROLE_MODE. --- include/channel.h | 20 ++++++++- modules/Makefile.in | 1 + modules/core/m_kick.c | 2 +- modules/m_grant.c | 102 ++++++++++++++++++++++++++++++++++++++++++ modules/m_topic.c | 2 +- src/channel.c | 3 ++ src/chmode.c | 25 +++++++++-- src/messages.tab | 2 +- 8 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 modules/m_grant.c diff --git a/include/channel.h b/include/channel.h index 80f7c0fb..12caaaf1 100644 --- a/include/channel.h +++ b/include/channel.h @@ -60,7 +60,7 @@ struct Channel rb_dlink_list members; /* channel members */ rb_dlink_list locmembers; /* local channel members */ -rb_dlink_list invites; + rb_dlink_list invites; rb_dlink_list banlist; rb_dlink_list exceptlist; rb_dlink_list invexlist; @@ -87,6 +87,7 @@ struct membership struct Channel *chptr; struct Client *client_p; unsigned int flags; + unsigned int roles; unsigned long bants; }; @@ -187,11 +188,26 @@ typedef int (*ExtbanFunc)(const char *data, struct Client *client_p, #define MODE_ADD 1 #define MODE_DEL -1 +/* Channel roles */ +#define CHANROLE_NONE 0x000 +#define CHANROLE_UNSET 0x001 /* Special value */ +#define CHANROLE_KICK 0x002 /* Can kick */ +#define CHANROLE_STATUS 0x004 /* Can change status modes */ +#define CHANROLE_GRANT 0x008 /* Can grant (unused atm) */ +#define CHANROLE_MODE 0x010 /* Can change modes */ +#define CHANROLE_TOPIC 0x020 /* Can change topic */ + #define SecretChannel(x) ((x) && ((x)->mode.mode & MODE_SECRET)) #define HiddenChannel(x) ((x) && ((x)->mode.mode & MODE_PRIVATE)) #define PubChannel(x) ((!x) || ((x)->mode.mode &\ (MODE_PRIVATE | MODE_SECRET)) == 0) +#define HasChanRole(m, r) (((m)->roles & r) != 0) +#define SetChanRole(m, r) ((m)->roles |= r) +#define RemoveChanRole(m, r) ((m)->roles &= ~r) + +#define IsChanRoleSet(m, r) + /* channel visible */ #define ShowChannel(v,c) (PubChannel(c) || IsMember((v),(c))) @@ -279,7 +295,7 @@ extern int match_extban(const char *banstr, struct Client *client_p, struct Chan extern int valid_extban(const char *banstr, struct Client *client_p, struct Channel *chptr, long mode_type); const char * get_extban_string(void); -extern int get_channel_access(struct Client *source_p, struct membership *msptr); +extern int get_channel_access(struct Client *source_p, struct membership *msptr, int role); extern void send_channel_join(struct Channel *chptr, struct Client *client_p); diff --git a/modules/Makefile.in b/modules/Makefile.in index e9936e97..8c3d4798 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -65,6 +65,7 @@ TSRCS = \ m_dline.c \ m_encap.c \ m_etrace.c \ + m_grant.c \ m_help.c \ m_info.c \ m_invite.c \ diff --git a/modules/core/m_kick.c b/modules/core/m_kick.c index eb72d1c9..e66f52e7 100644 --- a/modules/core/m_kick.c +++ b/modules/core/m_kick.c @@ -97,7 +97,7 @@ m_kick(struct Client *client_p, struct Client *source_p, int parc, const char *p return 0; } - if(get_channel_access(source_p, msptr) < CHFL_CHANOP) + if(get_channel_access(source_p, msptr, CHANROLE_KICK) < CHFL_CHANOP) { if(MyConnect(source_p)) { diff --git a/modules/m_grant.c b/modules/m_grant.c new file mode 100644 index 00000000..ebef661e --- /dev/null +++ b/modules/m_grant.c @@ -0,0 +1,102 @@ +/* + * charybdis: an advanced ircd + * m_grant: handle services grant commands + */ + +#include "stdinc.h" +#include "client.h" +#include "ircd.h" +#include "numeric.h" +#include "s_serv.h" +#include "send.h" +#include "msg.h" +#include "parse.h" +#include "modules.h" +#include "s_conf.h" +#include "hash.h" + +struct flag_list +{ + char *name; + int flag; +}; + +static struct flag_list flaglist[] = { + {"kick", CHANROLE_KICK}, + {"grant", CHANROLE_GRANT}, + {"mode", CHANROLE_MODE}, + {"topic", CHANROLE_TOPIC}, + {"status", CHANROLE_STATUS}, + {NULL, 0}, +}; + +static int me_grant(struct Client *, struct Client *, int, const char **); + +struct Message grant_msgtab = { + "GRANT", 0, 0, 0, MFLG_SLOW, + {mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_grant, 4}, mg_ignore} +}; + +mapi_clist_av1 grant_clist[] = { &grant_msgtab, NULL }; +DECLARE_MODULE_AV1(grant, NULL, NULL, grant_clist, NULL, NULL, "Charybdis development team"); + +/* + * me_grant + * + * parv[1] = channel + * parv[2] = target UID + * parv[3] = flag spec +*/ +static int +me_grant(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + struct Channel *chptr; + struct Client *target_p; + struct membership *msptr; + char *s, *t, *p; + + if (!(chptr = find_channel(parv[1]))) + return 0; + + if (!(target_p = find_person(parv[2]))) + return 0; + + /* Makes no sense to do this for non-local users */ + if(!MyClient(target_p)) + return 0; + + if (!(msptr = find_channel_membership(chptr, target_p))) + return 0; + + /* Unset */ + RemoveChanRole(msptr, CHANROLE_UNSET); + + t = LOCAL_COPY(parv[3]); + for (s = rb_strtok_r(t, " ", &p); s; s = rb_strtok_r(NULL, " ", &p)) + { + const char *priv = s + 1; /* The actual priv */ + struct flag_list *fl; + int flag = 0; + + /* Go through the flags list... */ + for(fl = flaglist; fl->name != NULL; fl++) + { + if (strcasecmp(fl->name, priv) == 0) + { + flag = fl->flag; + break; + } + } + + /* Ack no flag! */ + if (!flag) + continue; + + if (s[0] == '-') + RemoveChanRole(msptr, flag); + else if (s[0] == '+') + SetChanRole(msptr, flag); + } + + return 0; +} diff --git a/modules/m_topic.c b/modules/m_topic.c index 15cc4756..b175612e 100644 --- a/modules/m_topic.c +++ b/modules/m_topic.c @@ -118,7 +118,7 @@ m_topic(struct Client *client_p, struct Client *source_p, int parc, const char * } if(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || - get_channel_access(source_p, msptr) >= CHFL_CHANOP) && + get_channel_access(source_p, msptr, CHANROLE_TOPIC) >= CHFL_CHANOP) && (!MyClient(source_p) || can_send(chptr, source_p, msptr))) { diff --git a/src/channel.c b/src/channel.c index 5a134235..5a0c53de 100644 --- a/src/channel.c +++ b/src/channel.c @@ -236,6 +236,9 @@ add_user_to_channel(struct Channel *chptr, struct Client *client_p, int flags) msptr->client_p = client_p; msptr->flags = flags; + /* Default to no chanroles until services says we're something else */ + msptr->roles = CHANROLE_UNSET; + rb_dlinkAdd(msptr, &msptr->usernode, &client_p->user->channel); rb_dlinkAdd(msptr, &msptr->channode, &chptr->members); diff --git a/src/chmode.c b/src/chmode.c index a268fdc5..04be25db 100644 --- a/src/chmode.c +++ b/src/chmode.c @@ -187,7 +187,7 @@ cflag_orphan(char c_) } int -get_channel_access(struct Client *source_p, struct membership *msptr) +get_channel_access(struct Client *source_p, struct membership *msptr, int role) { hook_data_channel_approval moduledata; @@ -201,7 +201,24 @@ get_channel_access(struct Client *source_p, struct membership *msptr) moduledata.chptr = msptr->chptr; moduledata.msptr = msptr; moduledata.target = NULL; - moduledata.approved = is_chanop(msptr) ? CHFL_CHANOP : CHFL_PEON; + + if (is_chanop(msptr)) + { + /* Their chanrole is unset by GRANT, for backwards compat let them pass */ + if(HasChanRole(msptr, CHANROLE_UNSET)) + { + moduledata.approved = CHFL_CHANOP; + goto finish_access; + } + } + + /* Check if they have the proper role */ + if(HasChanRole(msptr, role)) + moduledata.approved = CHFL_CHANOP; + else + moduledata.approved = CHFL_PEON; + +finish_access: call_hook(h_get_channel_access, &moduledata); @@ -1200,7 +1217,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET)) { if((msptr = find_channel_membership(targptr, source_p)) == NULL || - get_channel_access(source_p, msptr) != CHFL_CHANOP) + get_channel_access(source_p, msptr, CHANROLE_MODE) != CHFL_CHANOP) { sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), me.name, source_p->name, targptr->chname); @@ -1618,7 +1635,7 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, mode_limit = 0; mode_limit_simple = 0; - alevel = get_channel_access(source_p, msptr); + alevel = get_channel_access(source_p, msptr, CHANROLE_MODE); /* Hide connecting server on netburst -- jilles */ if (ConfigServerHide.flatten_links && IsServer(source_p) && !has_id(source_p) && !HasSentEob(source_p)) diff --git a/src/messages.tab b/src/messages.tab index 63e85207..3bcf93a8 100644 --- a/src/messages.tab +++ b/src/messages.tab @@ -507,7 +507,7 @@ static const char * replies[] = { /* 479 ERR_BADCHANNAME */ "%s :Illegal channel name", /* 480 ERR_THROTTLE */ ":%s 480 %s %s :Cannot join channel (+j) - throttle exceeded, try again later", /* 481 ERR_NOPRIVILEGES, */ ":Permission Denied - You're not an IRC operator", -/* 482 ERR_CHANOPRIVSNEEDED, */ ":%s 482 %s %s :You're not a channel operator", +/* 482 ERR_CHANOPRIVSNEEDED, */ ":%s 482 %s %s :You're not a channel operator or have no privilege", /* 483 ERR_CANTKILLSERVER, */ ":You can't kill a server!", /* 484 ERR_ISCHANSERVICE */ ":%s 484 %s %s %s :Cannot kick or deop a network service", /* 485 ERR_BANNEDNICK, */ NULL,