/* Access list and level modification handling for ChanServ.
*
* IRC Services is copyright (c) 1996-2005 Andrew Church.
* E-mail: <achurch@
[Üye Olmadan Linkleri Göremezsiniz. Üye Olmak için TIKLAYIN...]urch.org>
* Parts written by Andrew Kempe and others.
* This program is free but copyrighted software; see the file COPYING for
* details.
*/
#include "services.h"
#include "modules.h"
#include "conffile.h"
#include "language.h"
#include "commands.h"
#include "modules/nickserv/nickserv.h"
#include "modules/operserv/operserv.h"
#include "chanserv.h"
#include "access.h"
#include "cs-local.h"
/*************************************************************************/
static Module *module;
static Module *module_chanserv;
static int levelinfo_maxwidth = 0;
/*************************************************************************/
/* Local functions. */
static void do_levels(User *u);
static void do_access(User *u);
static int access_del(ChannelInfo *ci, int num, int uacc);
static int access_del_callback(User *u, int num, va_list args);
static int access_list(User *u, int index, ChannelInfo *ci, int *sent_header);
static int access_list_callback(User *u, int num, va_list args);
/*************************************************************************/
static Command cmds[] = {
{ "ACCESS", do_access, NULL, -1, -1,-1 },
{ "LEVELS", do_levels, NULL, -1, -1,-1 },
{ NULL }
};
/*************************************************************************/
/***************************** Help display ******************************/
/*************************************************************************/
/* Callback to display help text for ACCESS, ACCESS LEVELS, LEVELS, and
* LEVELS DESC.
*/
static int do_help(User *u, const char *param)
{
int i;
const char *s;
if (stricmp(param, "ACCESS") == 0) {
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS);
if (find_module("chanserv/access-xop")) {
if (protocol_features & PF_HALFOP) {
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS_XOP_HALFOP,
ACCLEV_SOP, ACCLEV_AOP, ACCLEV_HOP, ACCLEV_VOP);
} else {
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS_XOP,
ACCLEV_SOP, ACCLEV_AOP, ACCLEV_VOP);
}
}
return 1;
} else if (strnicmp(param, "ACCESS", 6) == 0
&& isspace(param[6])
&& stricmp(param+7+strspn(param+7," \t"), "LEVELS") == 0
) {
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS_LEVELS,
ACCLEV_SOP, ACCLEV_AOP);
if (protocol_features & PF_HALFOP) {
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS_LEVELS_HALFOP,
ACCLEV_HOP);
}
notice_help(s_ChanServ, u, CHAN_HELP_ACCESS_LEVELS_END,
ACCLEV_VOP);
return 1;
} else if (strnicmp(param, "LEVELS", 6) == 0) {
s = (param+6) + strspn(param+6," \t");
if (!*s) {
notice_help(s_ChanServ, u, CHAN_HELP_LEVELS);
if (find_module("chanserv/access-xop")) {
if (protocol_features & PF_HALFOP)
notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_XOP_HOP);
else
notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_XOP);
}
notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_END);
return 1;
} else if (stricmp(s, "DESC") == 0) {
notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_DESC);
if (!levelinfo_maxwidth) {
for (i = 0; levelinfo[i].what >= 0; i++) {
int len = strlen(levelinfo[i].name);
if (len > levelinfo_maxwidth)
levelinfo_maxwidth = len;
}
}
for (i = 0; levelinfo[i].what >= 0; i++) {
if (!*levelinfo[i].name)
continue;
notice(s_ChanServ, u->nick, " %-*s %s",
levelinfo_maxwidth, levelinfo[i].name,
getstring(u->ngi, levelinfo[i].desc));
}
return 1;
}
}
return 0;
}
/*************************************************************************/
/************************** The ACCESS command ***************************/
/*************************************************************************/
static void do_access(User *u)
{
char *chan = strtok(NULL, " ");
char *cmd = strtok(NULL, " ");
char *nick = strtok(NULL, " ");
char *s = strtok(NULL, " ");
ChannelInfo *ci;
NickInfo *ni;
NickGroupInfo *ngi;
int16 level = 0;
int i;
int is_list; /* Is true when command is either LIST or COUNT */
int is_servadmin = is_services_admin(u);
is_list = (cmd && (stricmp(cmd, "LIST")==0 || stricmp(cmd, "COUNT")==0));
/* If LIST/COUNT, we don't *require* any parameters, but we can take any.
* If DEL, we require a nick and no level.
* Else (ADD), we require a level (which implies a nick). */
if (!cmd || (is_list ? 0 :
(stricmp(cmd,"DEL")==0) ? (!nick || s) : !s)) {
syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
} else if (!(ci = get_channelinfo(chan))) {
notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
} else if (!is_servadmin
&& !check_access_cmd(u, ci, "ACCESS", is_list ? "LIST" : cmd)) {
notice_lang(s_ChanServ, u, ACCESS_DENIED);
} else if (stricmp(cmd, "ADD") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
return;
}
level = atoi(s);
if (level == 0) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_NONZERO);
return;
} else if (level <= ACCLEV_INVALID || level >= ACCLEV_FOUNDER) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_RANGE,
ACCLEV_INVALID+1, ACCLEV_FOUNDER-1);
return;
}
switch (access_add(ci, nick, level,
is_servadmin ? ACCLEV_FOUNDER : get_access(u,ci), u->nick)) {
case RET_ADDED:
notice_lang(s_ChanServ, u, CHAN_ACCESS_ADDED, nick, chan, level);
send_cmd(s_ChanServ, "PRIVMSG #svslog %s , %s Nickine (%s) Kanalında (%d) olarak Yetki ekledi.",
u->nick, nick, chan, level);
send_cmd(s_ChanServ, "NOTICE %s %s size, %s kanalinda %d ayarinda access ekledi.", u->nick, nick, chan, level);
break;
case RET_CHANGED:
notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_CHANGED,
nick, chan, level);
send_cmd(s_ChanServ, "NOTICE %s %s Nicki %s Kanalında Yetkinizi değiştirdi.", u->nick, nick, chan);
send_cmd(s_ChanServ, "PRIVMSG #svslog %s Nicki %s Kanalında (%s) nin Yetkisini (%d) olarak değiştirdi.", u->nick, chan, nick);
break;
case RET_UNCHANGED:
notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_UNCHANGED,
nick, chan, level);
break;
case RET_LISTFULL:
notice_lang(s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT, CSAccessMax);
break;
case RET_NOSUCHNICK:
notice_lang(s_ChanServ, u, CHAN_ACCESS_NICKS_ONLY);
break;
case RET_NICKFORBID:
notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, nick);
break;
case RET_PERMISSION:
notice_lang(s_ChanServ, u, PERMISSION_DENIED);
break;
}
} else if (stricmp(cmd, "DEL") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
return;
}
/* Special case: is it a number/list? */
if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
int count, deleted, last = -1, perm = 0;
deleted = process_numlist(nick, &count, access_del_callback, u,
ci, &last, &perm, is_servadmin
? ACCLEV_FOUNDER : get_access(u,ci));
if (!deleted) {
if (perm) {
notice_lang(s_ChanServ, u, PERMISSION_DENIED);
} else if (count == 1) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_SUCH_ENTRY,
last, ci->name);
} else {
notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, ci->name);
}
return;
} else if (deleted == 1) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_ONE, ci->name);
} else {
notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_SEVERAL,
deleted, ci->name);
}
} else { /* Not a number/list; search for it as a nickname. */
ni = get_nickinfo(nick);
if (!ni) {
notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
return;
}
ngi = get_ngi(ni);
if (!ngi) {
notice_lang(s_ChanServ, u, INTERNAL_ERROR);
return;
}
ARRAY_SEARCH_SCALAR(ci->access, nickgroup, ngi->id, i);
if (i == ci->access_count) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_NOT_FOUND, nick, chan);
return;
}
switch (access_del(ci, i, is_servadmin
? ACCLEV_FOUNDER : get_access(u,ci))) {
case RET_DELETED:
notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED,
ni->nick, ci->name);
send_cmd(s_ChanServ, "PRIVMSG #svslog %s Nicki %s Kanalında (%s) Nickinin Yetkisini sildi.", u->nick, chan, nick);
send_cmd(s_ChanServ, "NOTICE %s %s Kullanıcısı %s Kanalında Sizi sildi.", u->nick, u->nick, chan);
break;
case RET_PERMISSION:
notice_lang(s_ChanServ, u, PERMISSION_DENIED);
return;
}
}
put_channelinfo(ci);
} else if (stricmp(cmd, "LIST") == 0) {
int sent_header = 0;
if (ci->access_count == 0) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
return;
}
if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
process_numlist(nick,NULL,access_list_callback,u,ci,&sent_header);
} else {
ARRAY_FOREACH (i, ci->access) {
if (nick && ci->access[i].nickgroup) {
if (!(ngi = get_ngi_id(ci->access[i].nickgroup))
|| !match_wild_nocase(nick, ngi_mainnick(ngi))
) {
continue;
}
}
access_list(u, i, ci, &sent_header);
}
}
if (!sent_header)
notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, chan);
} else if (stricmp(cmd, "COUNT") == 0) {
int count = 0;
ARRAY_FOREACH (i, ci->access) {
if (ci->access[i].nickgroup)
count++;
}
notice_lang(s_ChanServ, u, CHAN_ACCESS_COUNT, ci->name, count);
} else { /* Unknown command */
syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
}
}
/*************************************************************************/
static int access_del(ChannelInfo *ci, int num, int uacc)
{
ChanAccess *access = &ci->access[num];
if (!access->nickgroup)
return RET_NOENTRY;
if (uacc <= access->level)
return RET_PERMISSION;
access->nickgroup = 0;
return RET_DELETED;
}
/* `last' is set to the last valid index seen
* `perm' is incremented whenever a permission-denied error occurs
*/
static int access_del_callback(User *u, int num, va_list args)
{
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *last = va_arg(args, int *);
int *perm = va_arg(args, int *);
int uacc = va_arg(args, int);
if (num < 1 || num > ci->access_count)
return 0;
*last = num;
switch (access_del(ci, num-1, uacc)) {
case RET_DELETED:
return 1;
case RET_PERMISSION:
(*perm)++;
/* fall through */
default:
return 0;
}
}
/*************************************************************************/
static int access_list(User *u, int index, ChannelInfo *ci, int *sent_header)
{
ChanAccess *access = &ci->access[index];
NickGroupInfo *ngi;
if (!access->nickgroup)
return RET_NOENTRY;
if (!(ngi = get_ngi_id(access->nickgroup)))
return RET_INTERR;
if (!*sent_header) {
notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name);
*sent_header = 1;
}
notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_FORMAT,
index+1, access->level, ngi_mainnick(ngi), access->ekleyen);
return RET_LISTED;
}
static int access_list_callback(User *u, int num, va_list args)
{
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *sent_header = va_arg(args, int *);
if (num < 1 || num > ci->access_count)
return 0;
return access_list(u, num-1, ci, sent_header) > 0;
}
/*************************************************************************/
/************************** The LEVELS command ***************************/
/*************************************************************************/
static void do_levels(User *u)
{
char *chan = strtok(NULL, " ");
char *cmd = strtok(NULL, " ");
char *what = strtok(NULL, " ");
char *s = strtok(NULL, " ");
ChannelInfo *ci;
int16 level;
int i;
/* If SET, we want two extra parameters; if DIS[ABLE], we want only
* one; else, we want none.
*/
if (!cmd || ((stricmp(cmd,"SET")==0) ? !s :
(strnicmp(cmd,"DIS",3)==0) ? (!what || s) : !!what)) {
syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
} else if (!(ci = get_channelinfo(chan))) {
notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
} else if (!is_founder(u, ci) && !is_services_admin(u)) {
notice_lang(s_ChanServ, u, ACCESS_DENIED);
} else if (stricmp(cmd, "SET") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_LEVELS_READONLY);
return;
}
level = atoi(s);
if (level <= ACCLEV_INVALID || level >= ACCLEV_FOUNDER) {
notice_lang(s_ChanServ, u, CHAN_LEVELS_RANGE,
ACCLEV_INVALID+1, ACCLEV_FOUNDER-1);
return;
}
for (i = 0; levelinfo[i].what >= 0; i++) {
if (stricmp(levelinfo[i].name, what) == 0) {
if (!ci->levels)
reset_levels(ci, 1);
ci->levels[levelinfo[i].what] = level;
/* Special case: autoprotect ON -> autoowner ON */
if (levelinfo[i].what == CA_AUTOPROTECT)
ci->levels[CA_AUTOOWNER] = ACCLEV_FOUNDER;
notice_lang(s_ChanServ, u, CHAN_LEVELS_CHANGED,
levelinfo[i].name, chan, level);
put_channelinfo(ci);
return;
}
}
notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
} else if (stricmp(cmd, "DIS") == 0 || stricmp(cmd, "DISABLE") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_LEVELS_READONLY);
return;
}
for (i = 0; levelinfo[i].what >= 0; i++) {
if (stricmp(levelinfo[i].name, what) == 0) {
if (!ci->levels)
reset_levels(ci, 1);
ci->levels[levelinfo[i].what] = ACCLEV_INVALID;
/* Special case: autoprotect OFF -> autoowner OFF */
if (levelinfo[i].what == CA_AUTOPROTECT)
ci->levels[CA_AUTOOWNER] = ACCLEV_INVALID;
notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED,
levelinfo[i].name, chan);
put_channelinfo(ci);
return;
}
}
notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
} else if (stricmp(cmd, "LIST") == 0) {
int i;
notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, chan);
if (!levelinfo_maxwidth) {
for (i = 0; levelinfo[i].what >= 0; i++) {
int len = strlen(levelinfo[i].name);
if (len > levelinfo_maxwidth)
levelinfo_maxwidth = len;
}
}
for (i = 0; levelinfo[i].what >= 0; i++) {
int lev;
if (!*levelinfo[i].name)
continue;
if (ci->levels)
lev = ci->levels[levelinfo[i].what];
else
lev = def_levels[levelinfo[i].what];
if (lev == ACCLEV_INVALID)
notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
levelinfo_maxwidth, levelinfo[i].name);
else if (lev == ACCLEV_FOUNDER)
notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER,
levelinfo_maxwidth, levelinfo[i].name);
else
notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL,
levelinfo_maxwidth, levelinfo[i].name, lev);
}
} else if (stricmp(cmd, "RESET") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED);
return;
}
reset_levels(ci, 0);
put_channelinfo(ci);
notice_lang(s_ChanServ, u, CHAN_LEVELS_RESET, chan);
} else {
syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
}
}
/*************************************************************************/
/***************************** Module stuff ******************************/
/*************************************************************************/
const int32 module_version = MODULE_VERSION_CODE;
ConfigDirective module_config[] = {
{ NULL }
};
/*************************************************************************/
int init_module(Module *module_)
{
module = module_;
module_chanserv = find_module("chanserv/main");
if (!module_chanserv) {
module_log("Main ChanServ module not loaded");
return 0;
}
use_module(module_chanserv);
if (!register_commands(module_chanserv, cmds)) {
module_log("Unable to register commands");
exit_module(0);
return 0;
}
if (!add_callback(module_chanserv, "HELP", do_help)) {
module_log("Unable to add callbacks");
exit_module(0);
return 0;
}
return 1;
}
/*************************************************************************/
int exit_module(int shutdown_unused)
{
#ifdef CLEAN_COMPILE
shutdown_unused = shutdown_unused;
#endif
if (module_chanserv) {
remove_callback(module_chanserv, "HELP", do_help);
unregister_commands(module_chanserv, cmds);
unuse_module(module_chanserv);
module_chanserv = NULL;
}
return 1;
}
/*************************************************************************/