/*
* IRC - Internet Relay Chat, modules/m_join.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "msg.h"
#include "h.h"
#include "s_conf.h"
static char *newCliSJOINFmt = "%T %s";
static struct Message _msgtab[] = {
{MSG_JOIN, 0, MAXPARA, M_SLOW, 0L,
m_unregistered, m_join, m_join, m_join, m_ignore}
};
static char *token = TOK1_JOIN;
#ifndef STATIC_MODULES
char *_version = "$Revision: 1.7 $";
void _modinit(void)
{
mod_add_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = _msgtab;
}
void _moddeinit(void)
{
mod_del_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = NULL;
}
#else
void m_join_init(void)
{
mod_add_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = _msgtab;
}
#endif
/*
* * m_join * parv[0] = sender prefix * parv[1] = channel *
* parv[2] = channel password (key)
*/
int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
static char jbuf[BUFSIZE];
dlink_node *lp;
aChannel *chptr;
char *name, *key = NULL;
int i, flags = 0, chanlen = 0;
char *p = NULL, *p2 = NULL;
int newchan = 1;
int successful_join_count = 0;
if (!(sptr->user))
return 0;
if (parc < 2 || *parv[1] == '\0') {
send_me_numeric(sptr, ERR_NEEDMOREPARAMS, MSG_JOIN);
return 0;
}
*jbuf = '\0';
/*
* * Rebuild list of channels joined to be the actual result of the *
* JOIN. Note that "JOIN 0" is the destructive problem.
*/
for (i = 0, name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, (char *) NULL, ",")) {
/*
* pathological case only on longest channel name. * If not dealt
* with here, causes desynced channel ops * since ChannelExists()
* doesn't see the same channel * as one being joined. cute bug.
* Oct 11 1997, Dianora/comstud
*/
if (!check_channelname(sptr, (unsigned char *) name))
continue;
chanlen = strlen(name);
if (chanlen > CHANNELLEN) {
name[CHANNELLEN] = '\0';
chanlen = CHANNELLEN;
}
if (*name == '&' && !MyConnect(sptr))
continue;
if (*name == '0' && !atoi(name))
*jbuf = '\0';
else if (!IsChannelName(name)) {
if (MyClient(sptr))
send_me_numeric(sptr, ERR_NOSUCHCHANNEL, name);
continue;
}
if (*jbuf)
strncat(jbuf, ",", 1);
strncat(jbuf, name, chanlen);
}
/*
* (void)strcpy(parv[1], jbuf);
*/
p = NULL;
if (parv[2])
key = strtoken(&p2, parv[2], ",");
parv[2] = NULL; /* for m_names call later, parv[parc] * must == NULL */
for (name = strtoken(&p, jbuf, ","); name;
key = (key) ? strtoken(&p2, NULL, ",") : NULL, name = strtoken(&p, NULL, ",")) {
/*
* * JOIN 0 sends out a part for all channels a user * has
* joined.
*/
if (*name == '0' && !atoi(name)) {
if (sptr->user->channel.head == NULL)
continue;
lp = sptr->user->channel.head;
while (lp) {
chptr = lp->data;
lp = lp->next;
sendto_channel_butserv(chptr, sptr, TOK1_PART, 0, "");
remove_user_from_channel(sptr, chptr);
}
if (ServerOpts.anti_spambot) {
if (MyConnect(sptr) && !IsAnOper(sptr)) {
if (sptr->count_join_part >= GeneralOpts.spam_num) {
sendto_lev(SPAM_LEV, "User %^C is a possible spambot", sptr);
sptr->oper_warn_count_down = ServerOpts.oper_spam_countdown;
} else {
int t_delta;
if ((t_delta =
(NOW - sptr->last_part_time)) >
ServerOpts.join_leave_count_expire_time) {
int decrement_count;
decrement_count = (t_delta / ServerOpts.join_leave_count_expire_time);
if (decrement_count > sptr->count_join_part)
sptr->count_join_part = 0;
else
sptr->count_join_part -= decrement_count;
} else {
if ((NOW - (sptr->last_join_time)) < GeneralOpts.spam_time)
sptr->count_join_part++;
}
sptr->last_part_time = NOW;
}
}
}
sendto_match_servs(NULL, sptr, TOK1_JOIN, "0");
continue;
}
if (MyConnect(sptr)) {
if ((sptr->user->joined >= ChannelConf.max_channels_per_user) &&
(!IsAnOper(sptr) || (sptr->user->joined >= ChannelConf.max_channels_per_user * 3))) {
send_me_numeric(sptr, ERR_TOOMANYCHANNELS, name);
if (ServerOpts.anti_spambot && successful_join_count)
sptr->last_join_time = NOW;
return 0;
}
if (ServerOpts.anti_spambot) {
successful_join_count++;
if ((sptr->count_join_part >= GeneralOpts.spam_num)) {
if (sptr->oper_warn_count_down > 0)
sptr->oper_warn_count_down--;
else
sptr->oper_warn_count_down = 0;
if (sptr->oper_warn_count_down == 0) {
sendto_lev(SPAM_LEV, "User %^C trying to join %s is a possible spambot", sptr, name);
sptr->oper_warn_count_down = ServerOpts.oper_spam_countdown;
}
return 0;
/* Don't actually JOIN anything, but don't let spambot know that */
}
}
}
chptr = create_channel(sptr, name, &newchan, 1);
if (!chptr)
continue;
/*
* local client is first to enter previously nonexistent
* channel so make them (rightfully) the Channel Operator.
*/
flags = newchan ? CHFL_CHANOP : 0;
if (GeneralOpts.split)
flags = 0;
if (MyConnect(sptr) && (i = can_join(sptr, chptr, key))) {
if (i == -2)
continue;
send_me_numeric(sptr, i, chptr->chname);
if (ServerOpts.anti_spambot && (successful_join_count > 0))
successful_join_count--;
continue;
}
if (IsMember(sptr, chptr))
continue;
/*
* only complain when the user can join the channel,
* the channel is being created by this user,
* and this user is not allowed to be an op. - lucas
*/
if (!add_user_to_channel(chptr, sptr, flags))
continue;
if (!(*chptr->chname == '&')) {
if (MyClient(sptr) && flags == CHFL_CHANOP) {
char *cmodes_sm = NULL;
chptr->tsval = timeofday;
if (ChannelConf.default_extended_topic_limitation)
cmodes_sm = "+Tn";
else
cmodes_sm = "+tn";
/*
* we keep channel "creations" to the server sjoin format,
* so we can bounce modes and stuff if our ts is older.
*/
sendto_serv_butone(cptr, &me, TOK1_SJOIN, "%T %H %s :@%s",
chptr, chptr, cmodes_sm, parv[0]);
sendto_service(SERVICE_SEE_JOINS, 0, sptr, chptr, TOK1_JOIN, "");
} else if (MyClient(sptr)) {
sendto_serv_butone(cptr, sptr, TOK1_SJOIN, newCliSJOINFmt, chptr, chptr->chname);
sendto_service(SERVICE_SEE_JOINS, 0, sptr, chptr, TOK1_JOIN, "");
} else {
sendto_match_servs(chptr, cptr, TOK1_JOIN, ":%H", chptr);
}
}
/*
* notify all other users on the new channel
*/
sendto_channel_butserv_short(chptr, sptr, TOK1_JOIN);
if (flags == CHFL_CHANOP) {
char *cmodes_sm = NULL;
if (ChannelConf.default_extended_topic_limitation)
cmodes_sm = "+Tn";
else
cmodes_sm = "+tn";
sendto_channel_butserv(chptr, &me, TOK1_MODE, 0, "%s", cmodes_sm);
/*
* In the past, we used to send MODE_TOPICLIMIT here. This is
* now changed. MODE_EXTOPIC leads into, that no user in the
* channel, who is not +u or +a cannot set the topic. Being
* an op does not do anything.
* This behavior was required, since some people were joining
* channels and setting a new topic, before ChanServ was able
* to set the mode, since Services uses MergeChannelModes feature
* to set as many modes as possible in a single line, and this
* requires a delay.
*
* -TimeMr14C
*/
chptr->mode.mode |= MODE_NOPRIVMSGS;
chptr->mode.mode |= (ChannelConf.default_extended_topic_limitation ? MODE_EXTOPIC : MODE_TOPICLIMIT);
}
if (MyClient(sptr)) {
del_invite(sptr, chptr);
if (chptr->topic[0] != '\0') {
send_me_numeric(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
send_me_numeric(sptr, RPL_TOPICWHOTIME, chptr->chname,
IsChanHideOps(chptr) ? chptr->chname : chptr->
topic_nick, chptr->topic_time);
}
send_names(sptr, chptr);
}
}
if (ServerOpts.anti_spambot)
if (MyConnect(sptr) && successful_join_count)
sptr->last_join_time = NOW;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1