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,