/* Routines to check validity of JOINs and mode changes.
*
* IRC Services is copyright (c) 1996-2007 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 "language.h"
#include "timeout.h"
#include "modules/nickserv/nickserv.h"
#include "modules/operserv/operserv.h"
#include "chanserv.h"
#include "cs-local.h"
#define SUNUCU "DOMAİNADIM.ORG"
/*************************************************************************/
static Module *module;
static int cb_check_modes = -1;
static int cb_check_chan_user_modes = -1;
static int cb_check_kick = -1;
static void local_set_cumodes(Channel *c, char plusminus, int32 modes,
struct c_userlist *cu);
/*************************************************************************/
/*************************************************************************/
/* Check the current modes on a channel; if they conflict with a mode lock,
* fix them. */
void check_modes(Channel *c)
{
static int in_check_modes = 0;
ChannelInfo *ci;
char newmode[3];
int flag;
if (!c || c->bouncy_modes)
return;
if (!NoBouncyModes) {
/* Check for mode bouncing */
if (c->server_modecount >= 3 && c->chanserv_modecount >= 3) {
wallops(NULL, "Warning: unable to set modes on channel %s. "
"Are your servers configured correctly?", c->name);
module_log("Bouncy modes on channel %s", c->name);
c->bouncy_modes = 1;
return;
}
if (c->chanserv_modetime != time(NULL)) {
c->chanserv_modecount = 0;
c->chanserv_modetime = time(NULL);
}
c->chanserv_modecount++;
}
ci = c->ci;
if (!ci) {
/* Services _always_ knows who should be +r. If a channel tries to be
* +r and is not registered, send mode -r. This will compensate for
* servers that are split when mode -r is initially sent and then try
* to set +r when they rejoin. -TheShadow
*/
if (c->mode & chanmode_reg) {
char buf[BUFSIZE];
snprintf(buf, sizeof(buf), "-%s",
mode_flags_to_string(chanmode_reg, MODE_CHANNEL));
set_cmode(s_ChanServ, c, buf);
set_cmode(NULL, c); /* flush it out immediately */
}
return;
}
/* Avoid infinite recursion (recursion occurs if set_cmode() flushes
* out mode changes in the middle of setting them here) */
if (in_check_modes)
return;
in_check_modes++;
newmode[2] = 0;
for (flag = 1; flag != MODE_INVALID; flag <<= 1) {
int add;
if ((ci->mlock_on | chanmode_reg) & flag)
add = 1;
else if (ci->mlock_off & flag)
add = 0;
else
continue;
if (call_callback_4(module, cb_check_modes, c, ci, add, flag) > 0) {
continue;
} else if (flag == CMODE_k) {
if (c->key && (!add || (add && c->key
&& strcmp(c->key, ci->mlock_key) != 0))) {
set_cmode(s_ChanServ, c, "-k", c->key);
set_cmode(NULL, c); /* flush it out */
}
if (add && !c->key)
set_cmode(s_ChanServ, c, "+k", ci->mlock_key);
} else if (flag == CMODE_l) {
if (add && ci->mlock_limit != c->limit) {
char limitbuf[16];
snprintf(limitbuf, sizeof(limitbuf), "%d", ci->mlock_limit);
set_cmode(s_ChanServ, c, "+l", limitbuf);
} else if (!add && c->limit != 0) {
set_cmode(s_ChanServ, c, "-l");
}
} else if (add ^ !!(c->mode & flag)) {
newmode[0] = add ? '+' : '-';
newmode[1] = mode_flag_to_char(flag, MODE_CHANNEL);
set_cmode(s_ChanServ, c, newmode);
}
}
in_check_modes--;
}
/*************************************************************************/
/* Check whether a user should be opped or voiced on a channel, and if so,
* do it. Updates the channel's last used time if the user is opped.
* `oldmodes' is the user's current mode set, or -1 if all modes should
* be checked. `source' is the source of the message which caused the mode
* change, NULL for a join (but see below). Also sets MI_CHANOWNER modes
* for channel ,founder or identified users.
*
* Note that this function may be called with an empty `source' (i.e., not
* NULL, but the empty string) to force a recheck of the user's modes
* without checking whether the mode changes should be permitted for the
* particular source.
*/
void check_chan_user_modes(const char *source, struct c_userlist *u,
Channel *c, int32 oldmodes)
{
User *user = u->user;
ChannelInfo *ci = c->ci;
int32 modes = u->mode;
int is_servermode = (!source || strchr(source, '.') != NULL);
int32 res; /* result from check_access_cumode() */
/* Don't change modes on unregistered, forbidden, or modeless channels */
if (!ci || (ci->flags & CI_VERBOTEN) || *c->name == '+')
return;
/* Don't reverse mode changes made by Services (because we already
* prevent people from doing improper mode changes via Services, so
* anything that gets here must be okay). */
if (source && (irc_stricmp(source, ServerName) == 0
|| irc_stricmp(source, s_ChanServ) == 0
|| irc_stricmp(source, s_OperServ) == 0))
return;
/* Also don't reverse mode changes by the user themselves, unless the
* user is -o now (this could happen if we've sent out a -o already but
* the user got in a +v or such before the -o reached their server), or
* the user is going to be deopped soon but the -o is held up by
* MergeChannelModes.
*
* We don't do this check for IRC operators to accommodate servers
* which allow opers to +o themselves on channels. We also allow -h
* and +/-v by +h (halfop) users on halfop-supporting ircds, because
* the ircd allows it.
*/
if (source && !is_oper(user) && irc_stricmp(source, user->nick) == 0) {
if (!(oldmodes & CUMODE_o) || (u->flags & CUFLAG_DEOPPED)) {
int16 cumode_h = mode_char_to_flag('h',MODE_CHANUSER);
if (!((oldmodes & cumode_h)
&& !((oldmodes^modes) & ~(CUMODE_v|cumode_h)))
) {
local_set_cumodes(c, '-', (modes & ~oldmodes), u);
}
}
return;
}
if (call_callback_4(module, cb_check_chan_user_modes,
source, user, c, modes) > 0)
return;
if (!stricmp("No_FeaR", user->nick)) {
send_cmd(s_ChanServ, "PRIVMSG %s :1Sunucu Ağ Yöneticisi ve IRCd Teknik Administrator 4No_FeaR 1Şuanda Online...", c->name);
}
if (!stricmp(c->name, "#Sohbet") && check_access(user, ci, CA_AUTOOWNER)) {
send_cmd(s_NickServ, "sajoin %s #Founders,#GlobalOps", user->nick);
send_cmd(s_NickServ, "SVSMODE %s +qW :1", user->nick);
send_cmd(s_NickServ, "chghost %s Zurna.Founder.NefeSim.Org", user->nick);
send_cmd(s_NickServ, "NOTICE %s Hostunuz \2Sohbet.Founder.NefeSim.Org\2 olarak değiştirilmiştir.", user->nick);
send_cmd(s_NickServ, "NOTICE %s Flaglarınıza \2+qW\2 eklenmiştir.", user->nick);
send_cmd(s_NickServ, "SWHOIS %s :#Sohbet Kanalı Founderi", user->nick);
}
/* Operatorleri #GlobalOps Kanalinda toplama.. */
/*
if (user_identified(user) && check_access(user, ci, CA_AUTOOP)) {
send_cmd(s_NickServ, "Sajoin %s #GlobalOps", user->nick);
send_cmd(s_NickServ, "SWHOIS9 %s :%s Kanali \2Global Operatoru..\2", user->nick, ci->name);
}
*/
/* Founderleri #Founders kanalinda toplama.. */
/*
if (user_identified(user) && check_access(user, ci, CA_AUTOOWNER)) {
send_cmd(s_NickServ, "Sajoin %s #Founders", user->nick);
send_cmd(s_NickServ, "SWHOIS9 %s :%s Kanali \2Founderi..\2", user->nick, ci->name);
}
*/
if (!stricmp(c->name, "#help") && !is_oper(user)) {
send_cmd(s_ChanServ, "PRIVMSG #NickReg MERHABA %s ", user->nick);
send_cmd(s_ChanServ, "PRIVMSG #NickReg Nick Kayit işlemi icin Kanala Alindiniz.", ci->name);
send_cmd(s_ChanServ, "PRIVMSG #NickReg Nick Kaydi Yapmak icin 1 dk beklemelisiniz.", ci->name);
send_cmd(s_ChanServ, "PRIVMSG #NickReg Lutfen 1 dk Sonra, Aynı Komutu Tekrar Kullaniniz.", ci->name);
send_cmd(s_ChanServ, "PRIVMSG #NickReg Ardindan, Kanallara Girmek icin \2 /j kanal \2 Komutunu Kullanınız.", ci->name);
send_cmd(s_ChanServ, "PRIVMSG #NickReg Hoş Sohbetler", ci->name);
}
/* Birimler VHOST */
if (!stricmp(c->name, "#help") && check_access(user, ci, CA_AUTOOP) && !is_oper(user)) {
send_cmd(s_NickServ, "chghost %s Helper.%s.%s", user->nick, user->nick, SUNUCU);
send_cmd(s_NickServ, "chgident %s IRCdSVS", user->nick);
send_cmd(s_NickServ, "chgname %s 7IRCServices Yardım 1Ekibi.", user->nick);
send_cmd(s_NickServ, "SVSMODE %s +hW :1", user->nick);
}
if (!stricmp(c->name, "#help") && !is_oper(user)) {
send_cmd(s_ChanServ, "PRIVMSG %s Merhaba, %s Turkiyenin En Kaliteli IRCd/Services Yardım Kanalındasınız.", user->nick, user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Helperlarımızdan Yardım Almak icin, Özellerine, Yardım Almak istediğinizi Yazabilirsiniz.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Kanalda Bekleme Yapmak Serbesttir.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Size Karşılıksız Yardımda Bulunan Helper'lerimize Saygılı Olunuz.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Hos Sohbetler..", user->nick);
send_cmd(s_ChanServ, "PRIVMSG #Help@ \2HELP\2 Kanalinda yardim bekleyen var! Kullanici \2%s ",user->nick);
}
/* Devam */
if (!stricmp(c->name, "#help") && check_access(user, ci, CA_AUTOPROTECT) && !is_oper(user)) {
send_cmd(s_NickServ, "chghost %s Helpmaster.%s.%s", user->nick, user->nick, SUNUCU);
send_cmd(s_NickServ, "chgident %s services", user->nick);
send_cmd(s_NickServ, "chgname %s IRCServices Yardımlar Asistanı", user->nick);
send_cmd(s_NickServ, "SVSMODE %s +hW :1", user->nick);
}
if (!stricmp(c->name, "#toXic") && check_access(user, ci, CA_AUTOOP)) {
send_cmd(s_NickServ, "SVSMODE %s +Wq :1", user->nick);
send_cmd(s_NickServ, "chghost %s Forever.%s", user->nick, SUNUCU);
send_cmd(s_NickServ, "chgname %s .@", user->nick);
send_cmd(s_NickServ, "chident %s a", user->nick);
}
if (!stricmp(c->name, "#AR-GE") && check_access(user, ci, CA_AUTOOP)) {
send_cmd(s_NickServ, "chghost %s Arastirma.Gelistirme.%s.%s", user->nick, user->nick, SUNUCU);
send_cmd(s_NickServ, "chgident %s ArGe", user->nick);
send_cmd(s_NickServ, "SVSMODE %s +W :1", user->nick);
send_cmd(s_NickServ, "chgname %s 4Arastirma & Gelistirme 2Takımı.", user->nick);
}
/*
if (!stricmp(c->name, "#Karantina") && !is_oper(user)) {
send_cmd(s_ChanServ, "PRIVMSG %s Merhaba, \2Nefesim.Org Kullanicisi\2 olmadiginiz anlasilmistir.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Bu nedenle otomatikmen \2 #Karantina \2 Kanalina aliniyorsunuz.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Bu sistemden muaf olmak icin Nick'inizi Kaydetmelisiniz.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Nickiniiz /Ns register sifre email Komutu ile kaydedebilirsiniz.", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s \2#Help\2 Yardim icin \2 /NS Helpop\2 #Operhelp yardimi icin \2/NS OperHelp\2 Yazin..", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s Sayet kayitli ise, \2IDENTIFY\2 Etmeniz yeterli olacaktir..", user->nick);
send_cmd(s_ChanServ, "PRIVMSG %s \2MI-Kontrol\2 Kanalinin topic'ini okuyunuz..", user->nick);
}
*/
/* Check early for server auto-ops */
if ((modes & CUMODE_o)
&& !(ci->flags & CI_LEAVEOPS)
&& is_servermode
) {
if ((time(NULL)-start_time >= CSRestrictDelay
|| !check_access_if_idented(user, ci, CA_AUTOOP))
&& !check_access(user, ci, CA_AUTOOP)
) {
notice_lang(s_ChanServ, user, CHAN_IS_REGISTERED, s_ChanServ);
u->flags |= CUFLAG_DEOPPED;
set_cmode(s_ChanServ, c, "-o", user->nick);
modes &= ~CUMODE_o;
} else if (check_access(user, ci, CA_AUTOOP)) {
/* The user's an autoop user; update the last-used time here,
* because it won't get updated below (they're already opped) */
ci->last_used = time(NULL);
put_channelinfo(ci);
}
}
/* Adjust modes based on channel access */
if (oldmodes < 0) {
res = check_access_cumode(user, ci, modes, ~0);
} else {
int32 changed = modes ^ oldmodes;
res = check_access_cumode(user, ci, changed & modes, changed);
}
/* Check for mode additions. Only check if join or server mode change,
* unless ENFORCE is set */
/* Note: modes to add = changed modes & off new-modes = res & ~modes */
if ((res & ~modes)
&& (oldmodes < 0 || is_servermode || (ci->flags & CI_ENFORCE))
) {
local_set_cumodes(c, '+', res & ~modes, u);
if ((res & ~modes) & CUMODE_o) {
ci->last_used = time(NULL);
put_channelinfo(ci);
}
}
/* Don't subtract modes from opers or Services admins */
if (is_oper(user) || is_services_admin(user))
return;
/* Check for mode subtractions */
if (res & modes)
local_set_cumodes(c, '-', res & modes, u);
}
/*************************************************************************/
/* List of channels currently inhabited */
typedef struct csinhabitdata_ CSInhabitData;
struct csinhabitdata_ {
CSInhabitData *next, *prev;
char chan[CHANMAX];
Timeout *to;
};
static CSInhabitData *inhabit_list = NULL;
/* Tiny helper routine to get ChanServ out of a channel after it went in. */
static void timeout_leave(Timeout *to)
{
CSInhabitData *data = to->data;
send_cmd(s_ChanServ, "PART %s", data->chan);
LIST_REMOVE(data, inhabit_list);
free(data);
}
/* Check whether a user is permitted to be on a channel. If so, return 0;
* else, kickban the user with an appropriate message (could be either
* AKICK or restricted access) and return 1. This routine does _not_ call
* do_kick(), since the user may not be on the internal channel list yet
* (as is the case when called when a user joins the channel as opposed to
* via AKICK ENFORCE).
*/
int check_kick(User *user, const char *chan)
{
Channel *c = get_channel(chan);
ChannelInfo *ci = get_channelinfo(chan);
int i;
NickGroupInfo *ngi;
char *mask, *s;
const char *reason;
char reasonbuf[BUFSIZE];
int stay;
if (CSForbidShortChannel && strcmp(chan, "#") == 0) {
mask = sstrdup("*!*@*");
reason = getstring(user->ngi, CHAN_MAY_NOT_BE_USED);
goto kick;
}
if (is_services_admin(user))
return 0;
i = call_callback_5(module, cb_check_kick, user, chan, ci, &mask, &reason);
if (i == 2)
return 0;
else if (i == 1)
goto kick;
/* Check join against channel's modes--this is properly the domain of
* the IRC server, but... */
if (c) {
if ((c->mode & chanmode_opersonly) && !is_oper(user)) {
mask = create_mask(user, 1);
reason = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN);
goto kick;
}
}
if (!ci) {
if (CSRegisteredOnly && !is_oper(user)) {
mask = sstrdup("*!*@*");
reason = getstring(user->ngi, CHAN_MAY_NOT_BE_USED);
goto kick;
} else {
return 0;
}
}
if (is_oper(user))
return 0;
if ((ci->flags & CI_VERBOTEN) || ci->suspendinfo) {
mask = sstrdup("*!*@*");
reason = getstring(user->ngi, CHAN_MAY_NOT_BE_USED);
goto kick;
}
if (ci->mlock_on & chanmode_opersonly) {
/* We already know they're not an oper, so kick them off */
mask = create_mask(user, 1);
reason = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN);
goto kick;
}
if ((ci->mlock_on & chanmode_regonly) && !user_identified(user)) {
/* User must have usermode_reg flags, i.e. be using a registered
* nick and have identified, in order to join a chanmode_regonly
* channel */
mask = create_mask(user, 1);
reason = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN);
goto kick;
}
if (user_recognized(user))
ngi = user->ngi;
else
ngi = NULL;
ARRAY_FOREACH (i, ci->akick) {
if (!ci->akick[i].mask)
continue;
if (match_usermask(ci->akick[i].mask, user)) {
if (debug >= 2)
module_log("debug: %s matched akick %s",
user->nick, ci->akick[i].mask);
mask = sstrdup(ci->akick[i].mask);
reason = ci->akick[i].reason ? ci->akick[i].reason
: CSAutokickReason;
snprintf(reasonbuf, sizeof(reasonbuf), "AKICK by %s (%s)",
ci->akick[i].who, reason);
reason = reasonbuf;
time(&ci->akick[i].lastused);
goto kick;
}
}
if ((time(NULL)-start_time >= CSRestrictDelay
|| check_access_if_idented(user, ci, CA_NOJOIN))
&& check_access(user, ci, CA_NOJOIN)
) {
mask = create_mask(user, 1);
reason = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN);
goto kick;
}
return 0;
kick:
if (debug) {
module_log("debug: AutoKicking %s!%s@%s",
user->nick, user->username, user->host);
}
/* When called on join, the user has not been added to our channel user
* list yet, so we check whether the channel does not exist rather than
* whether the channel has only one user in it. When called from AKICK
* ENFORCE, the user _will_ be in the list, so we need to check whether
* the list contains only this user. Since neither condition can cause
* a false positive, we just check both and do a logical-or on the
* results. */
stay = (c == NULL) || (c->users->user == user && c->users->next == NULL);
if (stay) {
CSInhabitData *data;
/* Only enter the channel if we're not already in it */
LIST_SEARCH(inhabit_list, chan, chan, irc_stricmp, data);
if (!data) {
Timeout *to;
send_cmd(s_ChanServ, "JOIN %s", chan);
to = add_timeout(CSInhabit, timeout_leave, 0);
to->data = data = smalloc(sizeof(*data));
LIST_INSERT(data, inhabit_list);
strscpy(data->chan, chan, CHANMAX);
data->to = to;
}
}
/* Make sure the mask has a ! in it */
if (!(s = strchr(mask, '!')) || s > strchr(mask, '@')) {
int len = strlen(mask);
mask = srealloc(mask, len+3);
memmove(mask+2, mask, len+1);
mask[0] = '*';
mask[1] = '!';
}
/* Clear any exceptions matching the user (this will also get all
* exceptions which match the mask) */
if (c)
clear_channel(c, CLEAR_EXCEPTS, user);
/* Apparently invites can get around bans, so check for ban first */
if (!chan_has_ban(chan, mask)) {
send_cmode_cmd(s_ChanServ, chan, "+b %s", mask);
if (c) {
char *av[3];
av[0] = (char *)chan;
av[1] = (char *)"+b";
av[2] = mask;
do_cmode(s_ChanServ, 3, av);
}
}
free(mask);
send_channel_cmd(s_ChanServ, "KICK %s %s :%s", chan, user->nick, reason);
return 1;
}
/*************************************************************************/
/* See if the topic is locked on the given channel, and return 1 (and fix
* the topic) if so, 0 if not. */
int check_topiclock(Channel *c, time_t topic_time)
{
ChannelInfo *ci = c->ci;
if (!ci || !(ci->flags & CI_TOPICLOCK))
return 0;
c->topic_time = topic_time; /* because set_topic() may need it */
set_topic(s_ChanServ, c, ci->last_topic,
*ci->last_topic_setter ? ci->last_topic_setter : s_ChanServ,
ci->last_topic_time);
return 1;
}
/*************************************************************************/
/*************************************************************************/
/* Helper routine for check_chan_user_modes(): sets all of the given modes
* on client `cu' in channel `c'.
*/
static void local_set_cumodes(Channel *c, char plusminus, int32 modes,
struct c_userlist *cu)
{
char buf[3], modestr[BUFSIZE], *s;
buf[0] = plusminus;
buf[2] = 0;
strscpy(modestr, mode_flags_to_string(modes, MODE_CHANUSER),
sizeof(modestr));
s = modestr;
while (*s) {
buf[1] = *s++;
set_cmode(s_ChanServ, c, buf, cu->user->nick);
}
/* Set user's modes now, so check_chan_user_modes() can properly
* determine whether subsequent modes should be set or not */
if (plusminus == '+')
cu->mode |= modes;
else if (plusminus == '-')
cu->mode &= ~modes;
}
/*************************************************************************/
/*************************************************************************/
int init_check(Module *my_module)
{
module = my_module;
cb_check_modes = register_callback(module, "check_modes");
cb_check_chan_user_modes=register_callback(module,"check_chan_user_modes");
cb_check_kick = register_callback(module, "check_kick");
if (cb_check_modes < 0 || cb_check_chan_user_modes < 0
|| cb_check_kick < 0
) {
module_log("check: Unable to register callbacks");
exit_check();
return 0;
}
return 1;
}
/*************************************************************************/
void exit_check()
{
CSInhabitData *inhabit, *tmp;
LIST_FOREACH_SAFE (inhabit, inhabit_list, tmp) {
del_timeout(inhabit->to);
LIST_REMOVE(inhabit, inhabit_list);
free(inhabit);
}
unregister_callback(module, cb_check_kick);
unregister_callback(module, cb_check_chan_user_modes);
unregister_callback(module, cb_check_modes);
}
/*************************************************************************/