/* Autokick list handling.
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.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 "language.h"
#include "modules/nickserv/nickserv.h"
#include "modules/operserv/operserv.h"
#include "chanserv.h"
#include "cs-local.h"
/*************************************************************************/
static int akick_del(User *u, AutoKick *akick);
static int akick_del_callback(User *u, int num, va_list args);
static int akick_list(User *u, int index, ChannelInfo *ci, int *sent_header,
int is_view);
static int akick_list_callback(User *u, int num, va_list args);
/*************************************************************************/
/*************************************************************************/
void do_akick(User *u)
{
char *chan = strtok(NULL, " ");
char *cmd = strtok(NULL, " ");
char *mask = strtok(NULL, " ");
char *reason = strtok_remaining();
ChannelInfo *ci;
int i;
int is_list = (cmd && (stricmp(cmd,"LIST") == 0
|| stricmp(cmd,"VIEW") == 0
|| stricmp(cmd,"COUNT") == 0));
if (!cmd || (!mask &&
(stricmp(cmd, "ADD") == 0 || stricmp(cmd, "DEL") == 0))) {
syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_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 (!check_access_cmd(u, ci, "AKICK", is_list ? "LIST" : cmd)
&& !is_services_admin(u)) {
if (ci->founder && valid_ngi(u) && ci->founder == u->ngi->id)
notice_lang(s_ChanServ, u, CHAN_IDENTIFY_REQUIRED, s_ChanServ,
chan);
else
notice_lang(s_ChanServ, u, ACCESS_DENIED);
} else if (stricmp(cmd, "ADD") == 0) {
char *mask2, *user, *host;
const char *nick;
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
return;
}
/* Make sure we have a valid nick!user@host mask (fill in missing
* parts with "*"). Error out on @ in nick (also catches a@b!c),
* missing host, or empty nick/user/host. */
mask2 = sstrdup(mask);
nick = mask2;
user = strchr(mask2, '!');
if (user) {
*user++ = 0;
} else {
nick = "*";
user = mask2;
}
host = strchr(user, '@');
if (host)
*host++ = 0;
if (!*nick || !*user || !host || !*host || strchr(nick, '@')) {
notice_lang(s_ChanServ, u, BAD_NICKUSERHOST_MASK);
free(mask2);
return;
}
mask = smalloc(strlen(nick)+strlen(user)+strlen(host)+3);
sprintf(mask, "%s!%s@%s", nick, user, host);
free(mask2);
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
if (stricmp(ci->akick[i].mask, mask) == 0) {
notice_lang(s_ChanServ, u, CHAN_AKICK_ALREADY_EXISTS,
ci->akick[i].mask, chan);
return;
}
}
ARRAY_SEARCH_SCALAR(ci->akick, mask, NULL, i);
if (i == ci->akick_count) {
if (ci->akick_count >= CSAutokickMax) {
notice_lang(s_ChanServ, u, CHAN_AKICK_REACHED_LIMIT,
CSAutokickMax);
return;
}
ARRAY_EXTEND(ci->akick);
}
ci->akick[i].mask = mask;
ci->akick[i].reason = reason ? sstrdup(reason) : NULL;
time(&ci->akick[i].set);
ci->akick[i].lastused = 0;
strscpy(ci->akick[i].who, u->nick, NICKMAX);
notice_lang(s_ChanServ, u, CHAN_AKICK_ADDED, mask, chan);
put_channelinfo(ci);
} else if (stricmp(cmd, "DEL") == 0) {
if (readonly) {
notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
return;
}
/* Special case: is it a number/list? Only do search if it isn't. */
if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
int count, deleted, last = -1;
deleted = process_numlist(mask, &count, akick_del_callback, u,
ci, &last);
if (!deleted) {
if (count == 1) {
notice_lang(s_ChanServ, u, CHAN_AKICK_NO_SUCH_ENTRY,
last, ci->name);
} else {
notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH, ci->name);
}
return; /* don't bother with put_channelinfo() */
} else if (deleted == 1) {
notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED_ONE, ci->name);
} else {
notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED_SEVERAL,
deleted, ci->name);
}
} else {
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
if (stricmp(ci->akick[i].mask, mask) == 0)
break;
}
if (i < ci->akick_count) {
notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED, mask, chan);
akick_del(u, &ci->akick[i]);
} else {
notice_lang(s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask, chan);
return;
}
}
put_channelinfo(ci);
} else if (stricmp(cmd, "LIST") == 0 || stricmp(cmd, "VIEW") == 0) {
int is_view = stricmp(cmd,"VIEW")==0;
int sent_header = 0;
if (ci->akick_count == 0) {
notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, chan);
return;
}
if (mask && isdigit(*mask) &&
strspn(mask, "1234567890,-") == strlen(mask)) {
process_numlist(mask, NULL, akick_list_callback, u, ci,
&sent_header, is_view);
} else {
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
if (mask) {
if (!match_wild_nocase(mask, ci->akick[i].mask))
continue;
}
akick_list(u, i, ci, &sent_header, is_view);
}
}
if (!sent_header)
notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH, chan);
} else if (stricmp(cmd, "ENFORCE") == 0) {
Channel *c = get_channel(ci->name);
struct c_userlist *cu, *next;
char *argv[3];
int count = 0;
if (!c) {
notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, ci->name);
return;
}
LIST_FOREACH_SAFE (cu, c->users, next) {
if (check_kick(cu->user, c->name)) {
/* check_kick() will do the actual kick, but will not
* remove the user from the channel's user list or the
* user's channel list */
argv[0] = c->name;
argv[1] = cu->user->nick;
argv[2] = CSAutokickReason;
do_kick(s_ChanServ, 3, argv);
count++;
}
}
notice_lang(s_ChanServ, u, CHAN_AKICK_ENFORCE_DONE, chan, count);
} else if (stricmp(cmd, "COUNT") == 0) {
int count = 0, i;
ARRAY_FOREACH (i, ci->akick) {
if (ci->akick[i].mask)
count++;
}
notice_lang(s_ChanServ, u, CHAN_AKICK_COUNT, ci->name, count);
} else {
syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
}
}
/*************************************************************************/
/* `last' is set to the last index this routine was called with */
static int akick_del(User *u, AutoKick *akick)
{
if (!akick->mask)
return 0;
free(akick->mask);
free(akick->reason);
akick->mask = NULL;
akick->reason = NULL;
return 1;
}
static int akick_del_callback(User *u, int num, va_list args)
{
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *last = va_arg(args, int *);
*last = num;
if (num < 1 || num > ci->akick_count)
return 0;
return akick_del(u, &ci->akick[num-1]);
}
/*************************************************************************/
static int akick_list(User *u, int index, ChannelInfo *ci, int *sent_header,
int is_view)
{
AutoKick *akick = &ci->akick[index];
char buf[BUFSIZE];
if (!akick->mask)
return 0;
if (!*sent_header) {
notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name);
*sent_header = 1;
}
if (akick->reason)
snprintf(buf, sizeof(buf), " (%s)", akick->reason);
else
*buf = 0;
if (is_view) {
char setbuf[BUFSIZE], usedbuf[BUFSIZE];
strftime_lang(setbuf, sizeof(setbuf), u->ngi,
STRFTIME_DATE_TIME_FORMAT, akick->set);
if (akick->lastused) {
strftime_lang(usedbuf, sizeof(usedbuf), u->ngi,
STRFTIME_DATE_TIME_FORMAT, akick->lastused);
notice_lang(s_ChanServ, u, CHAN_AKICK_VIEW_FORMAT,
index+1, akick->mask,
akick->who[0] ? akick->who : "<unknown>",
setbuf, usedbuf, buf);
} else {
notice_lang(s_ChanServ, u, CHAN_AKICK_VIEW_UNUSED_FORMAT,
index+1, akick->mask,
akick->who[0] ? akick->who : "<unknown>", setbuf, buf);
}
} else {
notice(s_ChanServ, u->nick, " %3d %s%s", index+1, akick->mask, buf);
}
return 1;
}
static int akick_list_callback(User *u, int num, va_list args)
{
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *sent_header = va_arg(args, int *);
int is_view = va_arg(args, int);
if (num < 1 || num > ci->akick_count)
return 0;
return akick_list(u, num-1, ci, sent_header, is_view);
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1