/* Autokick list handling. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * 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 : "", setbuf, usedbuf, buf); } else { notice_lang(s_ChanServ, u, CHAN_AKICK_VIEW_UNUSED_FORMAT, index+1, akick->mask, akick->who[0] ? akick->who : "", 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); } /*************************************************************************/