/************************************************************************
* IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Computing Center
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* 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 1, 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.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: s_user.c,v 1.38 2005/01/11 09:08:46 skold Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define S_USER_C
#include "s_externs.h"
#undef S_USER_C
static char buf[BUFSIZE], buf2[BUFSIZE];
static int user_modes[] = { FLAGS_OPER, 'o',
FLAGS_LOCOP, 'O',
FLAGS_INVISIBLE, 'i',
FLAGS_WALLOP, 'w',
FLAGS_RESTRICTED, 'r',
FLAGS_AWAY, 'a',
#ifdef RUSNET_IRCD
FLAGS_VHOST, 'x',
#endif
0, 0 };
/*
** m_functions execute protocol messages on this server:
**
** cptr is always NON-NULL, pointing to a *LOCAL* client
** structure (with an open socket connected!). This
** identifies the physical socket where the message
** originated (or which caused the m_function to be
** executed--some m_functions may call others...).
**
** sptr is the source of the message, defined by the
** prefix part of the message if present. If not
** or prefix not found, then sptr==cptr.
**
** (!IsServer(cptr)) => (cptr == sptr), because
** prefixes are taken *only* from servers...
**
** (IsServer(cptr))
** (sptr == cptr) => the message didn't
** have the prefix.
**
** (sptr != cptr && IsServer(sptr) means
** the prefix specified servername. (?)
**
** (sptr != cptr && !IsServer(sptr) means
** that message originated from a remote
** user (not local).
**
** combining
**
** (!IsServer(sptr)) means that, sptr can safely
** taken as defining the target structure of the
** message in this server.
**
** *Always* true (if 'parse' and others are working correct):
**
** 1) sptr->from == cptr (note: cptr->from == cptr)
**
** 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
** *cannot* be a local connection, unless it's
** actually cptr!). [MyConnect(x) should probably
** be defined as (x == x->from) --msa ]
**
** parc number of variable parameter strings (if zero,
** parv is allowed to be NULL)
**
** parv a NULL terminated list of parameter pointers,
**
** parv[0], sender (prefix string), if not present
** this points to an empty string.
** parv[1]...parv[parc-1]
** pointers to additional parameters
** parv[parc] == NULL, *always*
**
** note: it is guaranteed that parv[0]..parv[parc-1] are all
** non-NULL pointers.
*/
/*
** next_client
** Local function to find the next matching client. The search
** can be continued from the specified client entry. Normal
** usage loop is:
**
** for (x = client; x = next_client(x,mask); x = x->next)
** HandleMatchingClient;
**
*/
aClient *next_client(next, ch)
Reg aClient *next; /* First client to check */
Reg char *ch; /* search string (may include wilds) */
{
Reg aClient *tmp = next;
next = find_client(ch, tmp);
if (tmp && tmp->prev == next)
return NULL;
if (next != tmp)
return next;
for ( ; next; next = next->next)
if (!match(ch,next->name) || !match(next->name,ch))
break;
return next;
}
/*
** hunt_server
**
** Do the basic thing in delivering the message (command)
** across the relays to the specific server (server) for
** actions.
**
** Note: The command is a format string and *MUST* be
** of prefixed style (e.g. ":%s COMMAND %s ...").
** Command can have only max 8 parameters.
**
** server parv[server] is the parameter identifying the
** target server.
**
** *WARNING*
** parv[server] is replaced with the pointer to the
** real servername from the matched client (I'm lazy
** now --msa).
**
** returns: (see #defines)
*/
int hunt_server(cptr, sptr, command, server, parc, parv)
aClient *cptr, *sptr;
char *command, *parv[];
int server, parc;
{
aClient *acptr;
/*
** Assume it's me, if no server
*/
if (parc <= server || BadPtr(parv[server]) ||
match(ME, parv[server]) == 0 ||
match(parv[server], ME) == 0)
return (HUNTED_ISME);
/*
** These are to pickup matches that would cause the following
** message to go in the wrong direction while doing quick fast
** non-matching lookups.
*/
if ((acptr = find_client(parv[server], NULL)))
if (acptr->from == sptr->from && !MyConnect(acptr))
acptr = NULL;
/* Match *.masked.servers */
if (!acptr && (acptr = find_server(parv[server], NULL)))
if (acptr->from == sptr->from && !MyConnect(acptr))
acptr = NULL;
/* Remote services@servers */
if (!acptr && (acptr = find_service(parv[server], NULL)))
if (acptr->from == sptr->from && !MyConnect(acptr))
acptr = NULL;
if (!acptr)
for (acptr = client, (void)collapse(parv[server]);
(acptr = next_client(acptr, parv[server]));
acptr = acptr->next)
{
if (acptr->from == sptr->from && !MyConnect(acptr))
continue;
/*
* Fix to prevent looping in case the parameter for
* some reason happens to match someone from the from
* link --jto
*/
if (IsRegistered(acptr) && (acptr != cptr))
break;
}
if (acptr)
{
if (!IsRegistered(acptr))
return HUNTED_ISME;
if (IsMe(acptr) || MyClient(acptr) || MyService(acptr))
return HUNTED_ISME;
if (match(acptr->name, parv[server]))
parv[server] = acptr->name;
if (IsService(sptr)
&& (IsServer(acptr->from)
&& match(sptr->service->dist,acptr->name) != 0))
{
sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
parv[server]);
return(HUNTED_NOSUCH);
}
sendto_one(acptr, command, parv[0],
parv[1], parv[2], parv[3], parv[4],
parv[5], parv[6], parv[7], parv[8]);
return(HUNTED_PASS);
}
sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]), parv[server]);
return(HUNTED_NOSUCH);
}
/*
** 'do_nick_name' ensures that the given parameter (nick) is
** really a proper string for a nickname (note, the 'nick'
** may be modified in the process...)
**
** RETURNS the length of the final NICKNAME (0, if
** nickname is illegal)
**
** Nickname characters are in range
** 'A'..'}', '_', '-', '0'..'9'
** anything outside the above set will terminate nickname.
** In addition, the first character cannot be '-'
** or a Digit.
** Finally forbid the use of "anonymous" because of possible
** abuses related to anonymous channnels. -kalt
**
** Note:
** '~'-character should be allowed, but
** a change should be global, some confusion would
** result if only few servers allowed it...
*/
int do_nick_name(nick, server)
unsigned char *nick;
int server;
{
Reg unsigned char *ch;
if (*nick == '-') /* first character '-' */
return 0;
if (isdigit(*nick) && !server) /* first character in [0..9] */
return 0;
if (!strncasecmp(nick, "anonymous", 9)) /* Do or do we not need anynimous? -skold*/
return 0;
for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
if (!isvalid(*ch) || isspace(*ch))
break;
#if defined(RUSNET_IRCD) && !defined(NO_DIRECT_VHOST)
if (!server && *ch == '!') /* for +x users at startup -skold */
ch++;
#endif
*ch = '\0';
return (ch - nick);
}
/*
** canonize
**
** reduce a string of duplicate list entries to contain only the unique
** items. Unavoidably O(n^2).
*/
char *canonize(buffer)
char *buffer;
{
static char cbuf[BUFSIZ];
Reg char *s, *t, *cp = cbuf;
Reg int l = 0;
char *p = NULL, *p2;
*cp = '\0';
for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ","))
{
if (l)
{
for (p2 = NULL, t = strtoken(&p2, cbuf, ","); t;
t = strtoken(&p2, NULL, ","))
if (!strcasecmp(s, t))
break;
else if (p2)
p2[-1] = ',';
}
else
t = NULL;
if (!t)
{
if (l)
*(cp-1) = ',';
else
l = 1;
(void)strcpy(cp, s);
if (p)
cp += (p - s);
}
else if (p2)
p2[-1] = ',';
}
return cbuf;
}
/*
** ereject_user
** extracted from register_user for clarity
** early rejection of a user connection, with logging.
*/
int
ereject_user(cptr, shortm, longm)
aClient *cptr;
char *shortm, *longm;
{
#if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \
(defined(USE_SYSLOG) && defined(SYSLOG_CONN))
sendto_flog(cptr, shortm, "<none>",
(IsUnixSocket(cptr)) ? me.sockhost :
((cptr->hostp) ? cptr->hostp->h_name : cptr->sockhost));
#endif
return exit_client(cptr, cptr, &me, longm);
}
/*
** register_user
** This function is called when both NICK and USER messages
** have been accepted for the client, in whatever order. Only
** after this the USER message is propagated.
**
** NICK's must be propagated at once when received, although
** it would be better to delay them too until full info is
** available. Doing it is not so simple though, would have
** to implement the following:
**
** 1) user telnets in and gives only "NICK foobar" and waits
** 2) another user far away logs in normally with the nick
** "foobar" (quite legal, as this server didn't propagate
** it).
** 3) now this server gets nick "foobar" from outside, but
** has already the same defined locally. Current server
** would just issue "KILL foobar" to clean out dups. But,
** this is not fair. It should actually request another
** nick from local user or kill him/her...
*/
int register_user(cptr, sptr, nick, username)
aClient *cptr;
aClient *sptr;
char *nick, *username;
{
Reg aConfItem *aconf;
aClient *acptr;
aServer *sp = NULL;
anUser *user = sptr->user;
short oldstatus = sptr->status;
char *parv[3], *s;
#ifndef NO_PREFIX
char prefix;
#endif
int i;
user->last = timeofday;
parv[0] = sptr->name;
parv[1] = parv[2] = NULL;
if (MyConnect(sptr))
{
char *reason = NULL;
#if defined(USE_IAUTH)
static time_t last = 0;
static u_int count = 0;
if (iauth_options & XOPT_EARLYPARSE && DoingXAuth(cptr))
{
cptr->flags |= FLAGS_WXAUTH;
/* fool check_pings() and give iauth more time! */
cptr->firsttime = timeofday;
cptr->lasttime = timeofday;
strncpyzt(sptr->user->username, username, USERLEN+1);
if (sptr->passwd[0])
sendto_iauth("%d P %s", sptr->fd,sptr->passwd);
sendto_iauth("%d U %s", sptr->fd, username);
return 1;
}
if (!DoneXAuth(sptr) && (iauth_options & XOPT_REQUIRED))
{
char *reason;
if (iauth_options & XOPT_NOTIMEOUT)
{
count += 1;
if (timeofday - last > 300)
{
sendto_flag(SCH_AUTH,
"iauth may not be running! (refusing new user connections)");
last = timeofday;
}
reason = "No iauth!";
}
else
reason = "iauth t/o";
sptr->exitc = EXITC_AUTHFAIL;
return ereject_user(cptr, reason,
"Authentication failure!");
}
if (timeofday - last > 300 && count)
{
sendto_flag(SCH_AUTH, "%d users rejected.", count);
count = 0;
}
/* this should not be needed, but there's a bug.. -kalt */
/* haven't seen any notice like this, ever.. no bug no more? */
if (*cptr->username == '\0')
{
sendto_flag(SCH_AUTH,
"Ouch! Null username for %s (%d %X)",
get_client_name(cptr, TRUE), cptr->fd,
cptr->flags);
sendto_iauth("%d E Null username [%s] %X", cptr->fd,
get_client_name(cptr, TRUE), cptr->flags);
return exit_client(cptr, sptr, &me,
"Fatal Bug - Try Again");
}
#endif
/*
** the following insanity used to be after check_client()
** but check_client()->attach_Iline() now needs to know the
** username for global u@h limits.
** moving this shit here shouldn't be a problem. -krys
** what a piece of $#@!.. restricted can only be known
** *after* attach_Iline(), so it matters and I have to move
** come of it back below. so global u@h limits really suck.
*/
#ifndef NO_PREFIX
/*
** ident is fun.. ahem
** prefixes used:
** none I line with ident
** ^ I line with OTHER type ident
** ~ I line, no ident
** + i line with ident
** = i line with OTHER type ident
** - i line, no ident
*/
if (!(sptr->flags & FLAGS_GOTID))
prefix = '~';
else
if (*sptr->username == '-' ||
index(sptr->username, '@'))
prefix = '^';
else
prefix = '\0';
/* OTHER type idents have '-' prefix (from s_auth.c), */
/* and they are not supposed to be used as userid (rfc1413) */
/* @ isn't valid in usernames (m_user()) */
if (sptr->flags & FLAGS_GOTID && *sptr->username != '-' &&
index(sptr->username, '@') == NULL)
strncpyzt(buf2, sptr->username, USERLEN+1);
else /* No ident, or unusable ident string */
/* because username may point to user->username */
strncpyzt(buf2, username, USERLEN+1);
if (prefix)
{
*user->username = prefix;
strncpy(&user->username[1], buf2, USERLEN);
}
else
strncpy(user->username, buf2, USERLEN+1);
user->username[USERLEN] = '\0';
/* eos */
#else
strncpyzt(user->username, username, USERLEN+1);
#endif
/* username cannot have control chars so truncate it
note: parv[1] cannot be NULL after checks above --LoSt */
for (s = user->username; *s; s++)
if (*s < 0x20 || *s == 0x7f)
{
*s = 0;
break;
}
if (user->username[0] == '\0'
#ifndef NO_PREFIX
|| prefix && user->username[1] == '\0'
#endif
)
{ /* control char in front -erra */
sendto_flag(SCH_AUTH,
"Ouch! Bad username for %s (%d %X)",
get_client_name(cptr, TRUE), cptr->fd,
cptr->flags);
sendto_iauth("%d E Bad username [%s] %X", cptr->fd,
get_client_name(cptr, TRUE), cptr->flags);
return exit_client(cptr, sptr, &me,
"Control chars in username denied");
}
if (sptr->exitc == EXITC_AREF || sptr->exitc == EXITC_AREFQ)
{
if (sptr->exitc == EXITC_AREF)
sendto_flag(SCH_LOCAL,
"Denied connection from %s (%s).",
get_client_host(sptr), sptr->name);
return ereject_user(sptr, " Denied ",
"Denied access");
}
if ((i = check_client(sptr)))
{
struct msg_set { char *shortm; char *longm; };
static struct msg_set exit_msg[7] = {
{ "G u@h max", "Too many user connections (global)" },
{ "G IP max", "Too many host connections (global)" },
{ "L u@h max", "Too many user connections (local)" },
{ "L IP max", "Too many host connections (local)" },
{ " max ", "Too many connections" },
{ " No Auth ", "Unauthorized connection" },
{ " Failure ", "Connect failure" } };
i += 7;
if (i < 0 || i > 6) /* in case.. */
i = 6;
ircstp->is_ref++;
sptr->exitc = EXITC_REF;
sendto_flag(SCH_LOCAL, "%s from %s.",
exit_msg[i].longm, get_client_host(sptr));
return ereject_user(cptr, exit_msg[i].shortm,
exit_msg[i].longm);
}
#ifndef NO_PREFIX
if (IsRestricted(sptr))
{
if (!(sptr->flags & FLAGS_GOTID))
prefix = '-';
else
if (*sptr->username == '-' ||
index(sptr->username, '@'))
prefix = '=';
else
prefix = '+';
*user->username = prefix;
strncpy(&user->username[1], buf2, USERLEN);
user->username[USERLEN] = '\0';
}
#endif
aconf = sptr->confs->value.aconf;
if (IsUnixSocket(sptr))
strncpyzt(user->host, me.sockhost, HOSTLEN+1);
else
strncpyzt(user->host, sptr->sockhost, HOSTLEN+1);
if (!BadPtr(aconf->passwd) &&
!StrEq(sptr->passwd, aconf->passwd))
{
ircstp->is_ref++;
sendto_one(sptr, err_str(ERR_PASSWDMISMATCH, parv[0]));
return exit_client(cptr, sptr, &me, "Bad Password");
}
bzero(sptr->passwd, sizeof(sptr->passwd));
/*
* following block for the benefit of time-dependent K:-lines
*/
if (find_kill(sptr, 1, &reason
#ifdef RUSNET_IRCD
, nick
#endif
))
{
/*char buf[100];*/
sendto_flag(SCH_LOCAL, "K-lined %s@%s.",
user->username, sptr->sockhost);
ircstp->is_ref++;
sptr->exitc = EXITC_REF;
#if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \
(defined(USE_SYSLOG) && defined(SYSLOG_CONN))
sendto_flog(sptr, " K lined ", user->username,
user->host);
#endif
if (reason)
sprintf(buf, "K-lined: %.80s", reason);
return exit_client(cptr, sptr, &me, (reason) ? buf :
"K-lined");
}
#ifdef R_LINES
if (find_restrict(sptr))
{
sendto_flag(SCH_LOCAL, "R-lined %s@%s.",
user->username, sptr->sockhost);
ircstp->is_ref++;
sptr->exitc = EXITC_REF;
# if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \
(defined(USE_SYSLOG) && defined(SYSLOG_CONN))
sendto_flog(sptr, " R lined ", user->username,
user->host);
# endif
return exit_client(cptr, sptr, &me , "R-lined");
}
#endif
#if defined(EXTRA_NOTICES) && defined(CLIENT_NOTICES)
sendto_flag(SCH_OPER, "Client connecting: %s (%s@%s)",
nick, user->username, user->host);
#endif
if (oldstatus == STAT_MASTER && MyConnect(sptr))
(void)m_oper(&me, sptr, 1, parv);
/* *user->tok = '1';
user->tok[1] = '\0';*/
sp = user->servp;
#if defined(RUSNET_IRCD) && !defined(NO_DIRECT_VHOST)
if (sptr->flags & FLAGS_XMODE)
{
sptr->flags &= ~FLAGS_XMODE;
user->flags |= FLAGS_VHOST;
}
#endif
}
else
{
strncpyzt(user->username, username, USERLEN+1);
#ifdef RUSNET_IRCD
strncpyzt(sptr->sockhost, user->host, HOSTLEN+1);
#endif
}
SetClient(sptr);
if (!MyConnect(sptr))
/* && IsServer(cptr)) -- obsolete, old 2.8 protocol;
someone needs to clean all this 2.8 stuff --Beeth */
{
acptr = find_server(user->server, NULL);
if (acptr && acptr->from != cptr)
{
sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s])",
ME, sptr->name, ME, user->server,
acptr->from->name, acptr->from->sockhost);
sptr->flags |= FLAGS_KILLED;
return exit_client(cptr, sptr, &me,
"USER server wrong direction");
}
}
#ifdef RUSNET_IRCD
#ifdef USE_SERVICES
if (strcmp(user->host, SERVICES_HOST)) /* don't touch services */
#endif
{ /*
** false data for usermode +x. After really long discussion
** it's been concluded to false last three octets of IP
** address and first two parts of hostname to provide
** the reasonable compromise between security and
** channels ban lists. Host.domain.tld is mapped to
** crc32(host.domain.tld).crc32(domain.tld).domain.tld
** and domain.tld to crc32(domain.tld).crc32(tld).domain.tld
** respectively --erra
**
** some modification there: eggdrop masking compatibility
** with the same hide availability
** Let's say crcsum() is crc32 of complete hostname. Then:
** a12.b34.sub.host.domain.tld -> crcsum().crc32(sub.host.domain.tld).host.domain.tld
** a12-b34.sub.host.domain.tld -> crcsum().crc32(sub.host.domain.tld).host.domain.tld
** comp.sub.host.domain.tld -> crcsum().crc32(sub.host.domain.tld).host.domain.tld
** a12.b34.host.domain.tld -> crcsum().crc32(b34.host.domain.tld).host.domain.tld
** a12-b34.host.domain.tld -> crcsum().crc32(crcsum()).host.domain.tld
** sub.host.domain.tld -> crcsum().crc32(crcsum()).host.domain.tld
** a12.b34.domain.tld -> crcsum().crc32(b34.domain.tld).domain.tld
** a12-b34.domain.tld -> crcsum().crc32(crcsum()).domain.tld
** host.domain.tld -> crcsum().crc32(crcsum()).domain.tld
** a12.dom2usr.tld -> crcsum().crc32(crcsum()).dom2usr.tld
** domain.tld -> crcsum().crc32(crcsum()).domain.tld
** domain. -> crcsum().crc32(domain.crcsum()).domain
**/
char *s = sptr->sockhost;
char *c = s + strlen(s);
int n = 0;
while (c > s)
{
c--;
if (*c == '.' && (++n) == 3) /* 4th dot reached */
break;
else if (*c <= '9' && *c >= '0')
break;
}
if (n) /* hostname second level or above... duh */
{
int len;
/* ignore digits in second-level domain part */
if (c > s && n == 1)
while (c > s && *c != '.')
c--;
else /* *c cannot reach \0 - see above */
while (*c != '.')
c++;
s = c;
while (s > sptr->sockhost && *(--s) != '.');
if (*s == '.') /* s is part for second crc32 */
s++;
if (s == sptr->sockhost) /* it needs crc32(crcsum()) */
s = user->host;
/* finished gathering data, let's rock */
strcpy(user->host, b64enc(gen_crc(sptr->sockhost)));
strcat(user->host, ".");
strcat(user->host, b64enc(gen_crc(s)));
len = strlen(user->host);
n = len + strlen(c) - HOSTLEN;
if (n > 0) /* overrun protection */
user->host[len - n] = '\0';
strcat(user->host, c);
}
else if (c == s) /* are there hosts w/o dots? Yes */
{
strcpy(user->host, sptr->sockhost);
#if 0 /* stupid masking, really */
strcat(user->host, ".");
strcpy(user->host, b64enc(gen_crc(user->host)));
strcat(user->host, ".");
strcat(user->host, b64enc(gen_crc(user->host)));
strcat(user->host, ".");
strcat(user->host, sptr->sockhost);
#endif
}
else /* IP address.. reverse search */
{
char *pfx;
s = strrchr(user->host, '.');
*s = '\0';
DupString(pfx, b64enc(gen_crc(user->host)));
s = strchr(user->host, '.'); /* keep 1st octet */
strcpy(++s, b64enc(gen_crc(sptr->sockhost)));
strcat(user->host, ".");
strcat(user->host, pfx);
strcat(user->host, ".in-addr");
MyFree(pfx);
}
}
#endif
send_umode(NULL, sptr, 0, SEND_UMODES, buf);
for (i = fdas.highest; i >= 0; i--)
{ /* Find my leaf servers and feed the new client to them */
if ((acptr = local[fdas.fd[i]]) == cptr || IsMe(acptr))
continue;
if ((aconf = acptr->serv->nline) &&
!match(my_name_for_link(ME, aconf->port),
user->server))
sendto_one(acptr, "NICK %s %d %s %s %s %s :%s",
nick, sptr->hopcount+1, user->username,
#ifdef RUSNET_IRCD
sptr->sockhost,
#else
user->host,
#endif
me.serv->tok, (*buf) ? buf : "+",
sptr->info);
else
sendto_one(acptr, "NICK %s %d %s %s %s %s :%s",
nick, sptr->hopcount+1, user->username,
#ifdef RUSNET_IRCD
sptr->sockhost,
#else
user->host,
#endif
user->servp->tok,
(*buf) ? buf : "+", sptr->info);
} /* for(my-leaf-servers) */
if (IsInvisible(sptr)) /* Can be initialized in m_user() */
istat.is_user[1]++; /* Local and server defaults +i */
else
istat.is_user[0]++;
#ifdef EXTRA_STATISTICS
/*
** Keep track of maximum number of global users.
*/
if ((istat.is_user[1] + istat.is_user[0]) > istat.is_m_users)
{
istat.is_m_users = istat.is_user[1] + istat.is_user[0];
if ((istat.is_m_users % 1000) == 0)
sendto_flag(SCH_NOTICE,
"New highest global client connection: %d",
istat.is_m_users);
}
#endif
if (MyConnect(sptr))
{
istat.is_unknown--;
istat.is_myclnt++;
#ifdef EXTRA_STATISTICS
/*
** Inform of highest client connection.
*/
if (istat.is_myclnt > istat.is_m_myclnt)
{
istat.is_m_myclnt = istat.is_myclnt;
if ((istat.is_m_myclnt % 10) == 0)
sendto_flag(SCH_NOTICE, "New highest local "
"client connection: %d",
istat.is_m_myclnt);
}
/*
** Small cludge to try and warn of some fast clonebots.
*/
if ((istat.is_myclnt % 10) == 0) {
if (istat.is_myclnt > istat.is_last_cnt) {
if (istat.is_last_cnt_t == 0)
istat.is_last_cnt_t = me.since;
sendto_flag(SCH_NOTICE, "Local increase from "
"%d to %d clients in %d second%s",
(istat.is_myclnt - 10),istat.is_myclnt,
(timeofday - istat.is_last_cnt_t),
((timeofday - istat.is_last_cnt_t == 1) ? ""
: "s"));
}
istat.is_last_cnt_t = timeofday;
istat.is_last_cnt = istat.is_myclnt;
}
#endif
sprintf(buf, "%s!%s@%s", nick, user->username,
#ifdef RUSNET_IRCD
(user->flags & FLAGS_VHOST) ?
#endif
user->host
#ifdef RUSNET_IRCD
: sptr->sockhost
#endif
);
sptr->exitc = EXITC_REG;
sendto_one(sptr, rpl_str(RPL_WELCOME, nick), buf);
/* This is a duplicate of the NOTICE but see below...*/
sendto_one(sptr, rpl_str(RPL_YOURHOST, nick),
get_client_name(&me, FALSE), version);
sendto_one(sptr, rpl_str(RPL_CREATED, nick), creation);
sendto_one(sptr, rpl_str(RPL_MYINFO, parv[0]),
ME, version);
#ifdef SEND_ISUPPORT
sendto_one(sptr, rpl_str(RPL_ISUPPORT, parv[0]), isupport);
#endif
(void)m_lusers(sptr, sptr, 1, parv);
(void)m_motd(sptr, sptr, 1, parv);
if (IsRestricted(sptr))
sendto_one(sptr, err_str(ERR_RESTRICTED, nick));
send_umode(sptr, sptr, 0, ALL_UMODES, buf);
#ifdef RUSNET_IRCD
if (sptr->transptr && sptr->transptr->id)
sendto_one(sptr, rpl_str(RPL_CODEPAGE, nick),
sptr->transptr->id);
#endif
nextping = timeofday;
}
#ifdef USE_SERVICES
#if 0
check_services_butone(SERVICE_WANT_NICK, user->server, NULL,
"NICK %s :%d", nick, sptr->hopcount+1);
check_services_butone(SERVICE_WANT_USER, user->server, sptr,
":%s USER %s %s %s :%s", nick, user->username,
user->host, user->server, sptr->info);
if (MyConnect(sptr)) /* all modes about local users */
send_umode(NULL, sptr, 0, ALL_UMODES, buf);
check_services_butone(SERVICE_WANT_UMODE, user->server, sptr,
":%s MODE %s :%s", nick, nick, buf);
#endif
if (MyConnect(sptr)) /* all modes about local users */
send_umode(NULL, sptr, 0, ALL_UMODES, buf);
check_services_num(sptr, buf);
#endif
return 1;
}
/*
** m_nick
** parv[0] = sender prefix
** parv[1] = nickname
** the following are only used between servers since version 2.9
** parv[2] = hopcount
** parv[3] = username (login name, account)
** parv[4] = client host name
** parv[5] = server token
** parv[6] = users mode
** parv[7] = users real name info
*/
int m_nick(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aClient *acptr;
int delayed = 0, l;
char nick[NICKLEN+2], *s, *user, *host;
Link *lp = NULL;
#ifdef RUSNET_IRCD
aChannel *chptr;
char *reason;
#endif
if (IsService(sptr))
{
sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
return 1;
}
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN, parv[0]));
return 1;
}
if (MyConnect(sptr) && (s = (char *)index(parv[1], '~')))
*s = '\0';
strncpyzt(nick, parv[1], NICKLEN+1);
if (parc == 8 && cptr->serv)
{
user = parv[3];
host = parv[4];
}
else
{
if (sptr->user)
{
user = sptr->user->username;
#ifdef RUSNET_IRCD
host = sptr->sockhost;
#else
host = sptr->user->host;
#endif
}
else
user = host = "";
}
/*
* if do_nick_name() returns a null name OR if the server sent a nick
* name and do_nick_name() changed it in some way (due to rules of nick
* creation) then reject it. If from a server and we reject it,
* and KILL it. -avalon 4/4/92
*/
l = do_nick_name(nick, IsServer(cptr) || IsMe(cptr));
#if defined(RUSNET_IRCD) && !defined(NO_DIRECT_VHOST)
if (l && nick[l - 1] == '!') /* wants usermode +x -skold */
{
l--;
nick[l] = '\0';
if (!IsServer(sptr) && !sptr->name[0])
sptr->flags |= FLAGS_XMODE;
}
#endif
if (l == 0 || (IsServer(cptr) && strcmp(nick, parv[1])))
{
sendto_one(sptr, err_str(ERR_ERRONEOUSNICKNAME, parv[0]),
parv[1]);
if (IsServer(cptr))
{
ircstp->is_kill++;
sendto_flag(SCH_KILL, "Bad Nick: %s From: %s %s",
parv[1], parv[0],
get_client_name(cptr, FALSE));
sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])",
ME, parv[1], ME, parv[1],
nick, cptr->name);
if (sptr != cptr) /* bad nick change */
{
sendto_serv_butone(cptr,
":%s KILL %s :%s (%s <- %s!%s@%s)",
ME, parv[0], ME,
get_client_name(cptr, FALSE),
parv[0], user, host);
sptr->flags |= FLAGS_KILLED;
return exit_client(cptr,sptr,&me,"BadNick");
}
}
return 2;
}
#ifdef RUSNET_IRCD
if (IsClient(cptr) && MyConnect(cptr))
{
chptr = rusnet_isagoodnickname(cptr, nick);
if (chptr != NULL)
{
sendto_one(sptr, err_str(ERR_7BIT, parv[0]),
chptr->chname);
return 2; /* NICK message ignored */
}
if (find_kill(sptr, 1, &reason, nick))
{
sendto_flag(SCH_LOCAL, "K-lined %s@%s.",
user, sptr->sockhost);
ircstp->is_kill++;
sptr->exitc = EXITC_REF;
#if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \
(defined(USE_SYSLOG) && defined(SYSLOG_CONN))
sendto_flog(sptr, " K lined ", user, host);
#endif
if (reason)
sprintf(buf, "K-lined: %.80s", reason);
exit_client(cptr, sptr, &me, (reason) ? buf :
"K-lined");
return 20; /* KILLed NICK entered */
}
}
#endif
/*
** Check against nick name collisions.
**
** Put this 'if' here so that the nesting goes nicely on the screen :)
** We check against server name list before determining if the nickname
** is present in the nicklist (due to the way the below for loop is
** constructed). -avalon
*/
if ((acptr = find_server(nick, NULL)))
if (MyConnect(sptr))
{
sendto_one(sptr, err_str(ERR_NICKNAMEINUSE, parv[0]),
nick);
return 2; /* NICK message ignored */
}
/*
** acptr already has result from previous find_server()
*/
if (acptr)
{
/*
** We have a nickname trying to use the same name as
** a server. Send out a nick collision KILL to remove
** the nickname. As long as only a KILL is sent out,
** there is no danger of the server being disconnected.
** Ultimate way to jupiter a nick ? >;-). -avalon
*/
sendto_flag(SCH_KILL,
"Nick collision on %s (%s@%s)%s <- (%s@%s)%s",
sptr->name,
(acptr->user) ? acptr->user->username : "???",
#ifdef RUSNET_IRCD
acptr->sockhost,
#else
(acptr->user) ? acptr->user->host : "???",
#endif
acptr->from->name, user, host,
get_client_name(cptr, FALSE));
ircstp->is_kill++;
sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)",
ME, sptr->name, ME, acptr->from->name,
/* NOTE: Cannot use get_client_name
** twice here, it returns static
** string pointer--the other info
** would be lost
*/
get_client_name(cptr, FALSE));
sptr->flags |= FLAGS_KILLED;
return exit_client(cptr, sptr, &me, "Nick/Server collision");
}
if (!(acptr = find_client(nick, NULL)))
{
#ifdef LOCKRECENTCHANGE
aClient *acptr2;
#endif
if (IsServer(cptr) || !(bootopt & BOOT_PROT))
goto nickkilldone;
#ifdef LOCKRECENTCHANGE
if ((acptr2 = get_history(nick, (long)(KILLCHASETIMELIMIT))) &&
!MyConnect(acptr2))
/*
** Lock nick for KCTL so one cannot nick collide
** (due to kill chase) people who recently changed
** their nicks. --Beeth
*/
delayed = 1;
else
#endif
/*
** Let ND work
*/
delayed = find_history(nick, (long)(DELAYCHASETIMELIMIT));
if (!delayed)
goto nickkilldone; /* No collisions, all clear... */
}
/*
** If acptr == sptr, then we have a client doing a nick
** change between *equivalent* nicknames as far as server
** is concerned (user is changing the case of his/her
** nickname or somesuch)
*/
if (acptr == sptr)
if (strcmp(acptr->name, nick) != 0)
/*
** Allows change of case in his/her nick
*/
goto nickkilldone; /* -- go and process change */
else
#ifdef RUSNET_IRCD
/*
** This is collision renaming coming from
** acptr owner direction (not the owner itself). Just delete this collision
** and forget it.
**/
if (acptr->flags & FLAGS_COLLMAP)
{
acptr->flags ^= FLAGS_COLLMAP;
if (!MyConnect(acptr) && acptr->from->serv)
del_from_collision_map(acptr->name, acptr->from->serv->crc);
else
sendto_flag(SCH_ERROR, "Found local %s in collision map",
acptr->name);
return 2;
} else
#endif
/*
** This is just ':old NICK old' type thing.
** Just forget the whole thing here. There is
** no point forwarding it to anywhere,
** especially since servers prior to this
** version would treat it as nick collision.
*/
return 2; /* NICK Message ignored */
/*
** Note: From this point forward it can be assumed that
** acptr != sptr (point to different client structures).
*/
/*
** If the older one is "non-person", the new entry is just
** allowed to overwrite it. Just silently drop non-person,
** and proceed with the nick. This should take care of the
** "dormant nick" way of generating collisions...
*/
if (acptr && IsUnknown(acptr) && MyConnect(acptr))
{
(void) exit_client(acptr, acptr, &me, "Overridden");
goto nickkilldone;
}
/*
** Decide, we really have a nick collision and deal with it
*/
if (!IsServer(cptr))
{
/*
** NICK is coming from local client connection. Just
** send error reply and ignore the command.
*/
sendto_one(sptr, err_str((delayed) ? ERR_UNAVAILRESOURCE
: ERR_NICKNAMEINUSE,
parv[0]), nick);
return 2; /* NICK message ignored */
}
/*
** NICK was coming from a server connection. Means that the same
** nick is registered for different users by different server.
** This is either a race condition (two users coming online about
** same time, or net reconnecting) or just two net fragments becoming
** joined and having same nicks in use. We cannot have TWO users with
** same nick--purge this NICK from the system with a KILL... >;)
*/
#ifdef RUSNET_IRCD
else
{
char *newnick = MyMalloc(NICKLEN + 1);
char *pparv[] = { acptr->name, newnick, NULL };
/*
** Recursive collision resolving is dangerous. We do not allow
** someone to collide interim nick change.
** Just kill that guy.
**
** Assuming that :NICK new is only possible for sptr here.
** But who knows.. We need some workaround for :old NICK :new thing anyway
*/
if (acptr->flags & FLAGS_COLLMAP && sptr == cptr) {
sendto_flag(SCH_HASH,
"Dropping fake collision %s (%s@%s) brought by %s",
acptr->name, user, host, sptr->from->name);
sendto_flag(SCH_KILL,
"Fake collision on %s (%s@%s)%s",
acptr->name, user, host, sptr->from->name);
ircstp->is_kill++;
/*
** We should send KILL back only, because this would kill
** collided nick otherwise
*/
sendto_one(cptr,
":%s KILL %s :%s (Fake collision)", ME, acptr->name, ME);
return -3;
}
/*
** Only :old NICK :new for sptr does make sense here (definitely)
*/
if (sptr->flags & FLAGS_COLLMAP) {
sendto_flag(SCH_HASH,
"Collision nick change for %s (%s@%s)%s has collided with %s (%s@%s)%s",
sptr->name, user, host, get_client_name(cptr, FALSE),
acptr->name, (acptr->user) ? acptr->user->username : "unknown",
acptr->sockhost, acptr->from->name);
sendto_flag(SCH_KILL,
"Recursive collision on %s (%s@%s)%s",
acptr->name,
(acptr->user) ? acptr->user->username : "unknown",
acptr->sockhost, acptr->from->name);
sendto_one(acptr, err_str(ERR_NICKCOLLISION, acptr->name),
acptr->name, user, host);
ircstp->is_kill++;
sendto_serv_butone(NULL,
":%s KILL %s :%s (Recursive collision)", ME, acptr->name, ME);
acptr->flags |= FLAGS_KILLED;
(void)exit_client(NULL, acptr, &me, "Recursive collision");
goto nickkilldone;
}
/* Force nick change -erra
** There are two options we have: either to change to
** nick + base64(crc32) the same as all interim changes we
** already made or to nick + 5 random digits.
** The latter is in force now.
** They are compatible to each other and can work in the same net together.
*/
if (MyConnect(acptr))
{
int i;
char *ch;
char *chasenick = MyMalloc(NICKLEN + 1);
*chasenick = '1';
strncpy(chasenick + 1, acptr->name, NICKLEN - 6);
chasenick[NICKLEN - 5] = '\0';
strcat(chasenick, b64enc(me.serv->crc));
strncpy(newnick, acptr->name, NICKLEN);
/*
** This is an ugly hack. We have to mark collided nick
** off in history as being mapped not actually hashing
** and renaming our client because we expect kill chase. -skold
*/
sendto_flag(SCH_HASH, "Adding history for %s", chasenick);
strncpyzt(acptr->name, chasenick, NICKLEN + 1);
add_history(acptr, acptr);
strncpyzt(acptr->name, newnick, NICKLEN + 1);
MyFree(chasenick);
/* Reserve 5-digits space */
newnick[NICKLEN - 4] = '\0';
ch = newnick + strlen(newnick);
#endif
#ifndef RUSNET_IRCD
sendto_one(acptr, err_str(ERR_NICKCOLLISION, acptr->name),
acptr->name, user, host);
#endif
#ifdef RUSNET_IRCD
/* cut last five digits only if there are exactly five */
for (i = 0, ch--; i < 5 && isdigit(*ch); i++, ch--);
if (i >= 5)
ch[1] = '\0';
SPRINTF (newnick + strlen(newnick), "%d",
10000 + (int) (60000.0 * rand() / (RAND_MAX + 10000.0)));
m_nick(acptr, acptr, 2, pparv);
}
else
#ifdef USE_SERVICES
if (acptr->user->servp->crc != invincible)
#endif
{
/*
** Here goes interim nick change to nick + base64(crc32) based
** on the server name owning this nick.
** For one-time collision renaming we need to comment out the true part above
** and uncomment what is commented out below.
*/
*newnick = '1';
/* guard against too long nick */
strncpy(newnick + 1, acptr->name, NICKLEN - 6);
newnick[NICKLEN - 5] = '\0';
strcat(newnick, b64enc(acptr->user->servp->crc));
/*
** Before rising collision we need to check if this
** collision can cycle. If it cycles we should send kill
** for parv[1] to everyone except cptr instead of collision
** resolving and let the current nick change complete -skold
**/
if (cptr != sptr) /* We need this workaround for NICK 2 form only */
{
aClient *bcptr;
if ((bcptr = find_client(newnick, (aClient *)NULL)) &&
bcptr == sptr)
{
sendto_flag(SCH_HASH, "Collision cycles on nick "
"%s(%s@%s)%s when renaming to %s(%s@%s)%s",
sptr->name, user, host,
get_client_name(cptr, FALSE), acptr->name,
(acptr->user) ?
acptr->user->username : "unknown",
acptr->sockhost, acptr->from->name);
sendto_flag(SCH_KILL, "Collision deadlock on "
"%s -> %s(%s@%s)%s -> %s", sptr->name,
acptr->name, (acptr->user) ?
acptr->user->username : "unknown",
acptr->sockhost, acptr->from->name, bcptr->name);
ircstp->is_kill++;
sendto_serv_butone(cptr, ":%s KILL %s :%s "
"(Collision deadlock)", ME, acptr->name, ME);
acptr->flags |= FLAGS_KILLED;
(void)exit_client(NULL, acptr, &me,
"Collision deadlock");
goto nickkilldone;
}
}
add_to_collision_map(acptr->name, newnick,
acptr->from->serv->crc);
acptr->flags |= FLAGS_COLLMAP;
m_nick(cptr, acptr, 2, pparv);
}
#ifdef USE_SERVICES
else
{
/*
** We should send kill to cptr for mapped nick because acptr->name
** is already owned by someone else (e.g. NickServ ;) -slcio
*/
*newnick = '1';
strncpy(newnick + 1, acptr->name, NICKLEN - 6);
newnick[NICKLEN - 5] = '\0';
if (cptr != sptr) {
strcat(newnick, b64enc(sptr->user->servp->crc));
} else {
aServer *asptr = NULL;
if (!(asptr = find_tokserver(atoi(parv[5]), cptr, NULL))) {
sendto_flag(SCH_ERROR,
"ERROR: USER:%s without SERVER:%s from %s",
parv[0], parv[5], get_client_name(cptr, FALSE));
ircstp->is_nosrv++;
return exit_client(NULL, sptr, &me, "No Such Server");
} else {
strcat(newnick, b64enc(asptr->crc));
sendto_flag(SCH_HASH, "Sending KILL for service collision victim: %s on %s",
newnick, find_server_string(asptr->snum));
}
}
sendto_flag(SCH_KILL,
"Services collision %s (%s@%s)%s",
newnick, user, sptr->sockhost, sptr->from->name);
ircstp->is_kill++;
sendto_one(cptr,
":%s KILL %s :%s (Services collision)", ME, newnick, ME);
if (cptr != sptr) {
sendto_serv_butone(NULL,
":%s KILL %s :%s (Services collision)", ME, sptr->name, ME);
sptr->flags |= FLAGS_KILLED;
return exit_client(cptr, sptr, &me, "Services collision");
} else {
return -3;
}
}
#endif
/* propagate to common channels and servers
* excluding source server direction for this nick (cptr)
*/
MyFree( newnick );
}
#endif
#ifndef RUSNET_IRCD
/*
** This seemingly obscure test (sptr == cptr) differentiates
** between "NICK new" (TRUE) and ":old NICK new" (FALSE) forms.
*/
if (sptr == cptr)
{
sendto_flag(SCH_KILL,
"Nick collision on %s (%s@%s)%s <- (%s@%s)%s",
acptr->name,
(acptr->user) ? acptr->user->username : "???",
(acptr->user) ? acptr->user->host : "???",
acptr->from->name,
user, host, get_client_name(cptr, FALSE));
/*
** A new NICK being introduced by a neighbouring
** server (e.g. message type "NICK new" received)
*/
ircstp->is_kill++;
sendto_serv_butone(NULL,
":%s KILL %s :%s ((%s@%s)%s <- (%s@%s)%s)",
ME, acptr->name, ME,
(acptr->user) ? acptr->user->username:"???",
(acptr->user) ? acptr->user->host : "???",
acptr->from->name, user, host,
/* NOTE: Cannot use get_client_name twice
** here, it returns static string pointer:
** the other info would be lost
*/
get_client_name(cptr, FALSE));
acptr->flags |= FLAGS_KILLED;
return exit_client(NULL, acptr, &me, "Nick collision");
}
/*
** A NICK change has collided (e.g. message type
** ":old NICK new". This requires more complex cleanout.
** Both clients must be purged from this server, the "new"
** must be killed from the incoming connection, and "old" must
** be purged from all outgoing connections.
*/
sendto_flag(SCH_KILL, "Nick change collision %s!%s@%s to %s %s <- %s",
sptr->name, user, host, acptr->name, acptr->from->name,
get_client_name(cptr, FALSE));
ircstp->is_kill++;
sendto_serv_butone(NULL, /* KILL old from outgoing servers */
":%s KILL %s :%s (%s@%s[%s](%s) <- %s@%s[%s])",
ME, sptr->name, ME, acptr->user->username,
acptr->user->host,
acptr->from->name, acptr->name,
user, host, cptr->name);
ircstp->is_kill++;
sendto_serv_butone(NULL, /* Kill new from incoming link */
":%s KILL %s :%s (%s@%s[%s] <- %s@%s[%s](%s))",
ME, acptr->name, ME, acptr->user->username,
acptr->user->host,
acptr->from->name,
user, host, cptr->name, sptr->name);
acptr->flags |= FLAGS_KILLED;
(void)exit_client(NULL, acptr, &me, "Nick collision(new)");
sptr->flags |= FLAGS_KILLED;
return exit_client(cptr, sptr, &me, "Nick collision(old)");
#endif /* !RUSNET_IRCD */
nickkilldone:
if (IsServer(sptr))
{
char *pv[7];
if (parc != 8)
{
sendto_flag(SCH_NOTICE,
"Bad NICK param count (%d) for %s from %s via %s",
parc, parv[1], sptr->name,
get_client_name(cptr, FALSE));
sendto_one(cptr, ":%s KILL %s :%s (Bad NICK %d)",
ME, nick, ME, parc);
return 0;
}
/* A server introducing a new client, change source */
sptr = make_client(cptr);
add_client_to_list(sptr);
if (parc > 2)
sptr->hopcount = atoi(parv[2]);
(void)strcpy(sptr->name, nick);
pv[0] = sptr->name;
pv[1] = parv[3];
pv[2] = parv[4];
pv[3] = parv[5];
pv[4] = parv[7];
pv[5] = parv[6];
pv[6] = NULL;
(void)add_to_client_hash_table(nick, sptr);
return m_user(cptr, sptr, 6, pv);
}
else if (sptr->name[0]) /* NICK received before, changing */
{
if (MyConnect(sptr))
{
if (!IsPerson(sptr)) /* Unregistered client */
return 2; /* Ignore new NICKs */
if (IsRestricted(sptr))
{
sendto_one(sptr,
err_str(ERR_RESTRICTED, nick));
return 2;
}
/* Can the user speak on all channels? */
for (lp = sptr->user->channel; lp; lp = lp->next)
if (can_send(sptr, lp->value.chptr) &&
!IsQuiet(lp->value.chptr))
break;
}
/*
** Client just changing his/her nick. If he/she is
** on a channel, send note of change to all clients
** on that channel. Propagate notice to other servers.
*/
/*
** :old NICK :new does not make sense when nick is collided -skold
*/
// sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
sendto_common_channels(sptr, ":%s NICK :%s", sptr->name, nick);
#if defined(EXTRA_NOTICES) && defined(NCHANGE_NOTICES)
if (MyConnect(sptr) && IsRegisteredUser(sptr))
sendto_flag(SCH_OPER,
"Nick change: From %s to %s (%s@%s)",
parv[0], nick, sptr->user->username,
#ifdef RUSNET_IRCD
sptr->sockhost
#else
sptr->user->host
#endif
);
#endif
if (sptr->user) /* should always be true.. */
{
add_history(sptr, sptr);
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_NICK,
sptr->user->server, sptr,
":%s NICK :%s", parv[0], nick);
#endif
}
else
sendto_flag(SCH_NOTICE,
"Illegal NICK change: %s -> %s from %s",
parv[0], nick, get_client_name(cptr,TRUE));
#ifdef RUSNET_IRCD
if (sptr->flags & FLAGS_COLLMAP) /* dealing with collision */
{
if (cptr == sptr->from)
{
sptr->flags ^= FLAGS_COLLMAP;
if (!MyConnect(sptr) && sptr->from->serv)
del_from_collision_map(sptr->name, sptr->from->serv->crc);
else
sendto_flag(SCH_ERROR, "Found local %s in collision map",
sptr->name);
}
else
cptr = sptr->from;
}
#endif
sendto_serv_butone(cptr, ":%s NICK :%s", sptr->name, nick);
if (sptr->name[0])
(void)del_from_client_hash_table(sptr->name, sptr);
(void)strcpy(sptr->name, nick);
}
else
{
/* Client setting NICK the first time */
/* This had to be copied here to avoid problems.. */
(void)strcpy(sptr->name, nick);
if (sptr->user)
/*
** USER already received, now we have NICK.
** *NOTE* For servers "NICK" *must* precede the
** user message (giving USER before NICK is possible
** only for local client connection!). register_user
** may reject the client and call exit_client for it
** --must test this and exit m_nick too!!!
*/
if (register_user(cptr, sptr, nick,
sptr->user->username)
== FLUSH_BUFFER)
return FLUSH_BUFFER;
}
/*
** Finally set new nick name.
*/
(void)add_to_client_hash_table(nick, sptr);
if (lp)
return 15;
else
return 3;
}
/*
** m_message (used in m_private() and m_notice())
** the general function to deliver MSG's between users/channels
**
** parv[0] = sender prefix
** parv[1] = receiver list
** parv[2] = message text
**
** massive cleanup
** rev argv 6/91
**
*/
static int m_message(cptr, sptr, parc, parv, notice)
aClient *cptr, *sptr;
char *parv[];
int parc, notice;
{
Reg aClient *acptr;
Reg char *s;
aChannel *chptr;
char *nick, *server, *p, *cmd, *user, *host;
int count = 0, penalty = 0, flag = 0;
cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]), cmd);
return 1;
}
if (parc < 3 || *parv[2] == '\0')
{
sendto_one(sptr, err_str(ERR_NOTEXTTOSEND, parv[0]));
return 1;
}
if (MyConnect(sptr))
parv[1] = canonize(parv[1]);
for (p = NULL, nick = strtoken(&p, parv[1], ","); nick;
nick = strtoken(&p, NULL, ","), penalty++)
{
/*
** restrict destination list to MAXPENALTY/2 recipients to
** solve SPAM problem --Yegg
*/
if (2*penalty >= MAXPENALTY) {
if (!notice)
sendto_one(sptr, err_str(ERR_TOOMANYTARGETS,
parv[0]),
"Too many",nick,"No Message Delivered");
continue;
}
/*
** nickname addressed?
*/
if ((acptr = find_person(nick, NULL)))
{
if (!notice && MyConnect(sptr) &&
acptr->user && (acptr->user->flags & FLAGS_AWAY))
sendto_one(sptr, rpl_str(RPL_AWAY, parv[0]),
acptr->name,
(acptr->user->away) ?
acptr->user->away : "Gone");
sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
parv[0], cmd, nick, parv[2]);
continue;
}
/*
** channel msg?
*/
if ((IsPerson(sptr) || IsService(sptr) || IsServer(sptr)) &&
(chptr = find_channel(nick, NullChn)))
{
if (IsServer(sptr) ||
(flag = can_send(sptr, chptr)) == 0)
sendto_channel_butone(cptr, sptr, chptr,
(*nick == '@') ? 1 : 0,
":%s %s %s :%s",
parv[0], cmd, nick,
parv[2]);
#ifdef RUSNET_IRCD
else if (flag == MODE_NOCOLOR)
if(strchr(parv[2],0x03))
sendto_one(sptr, err_str(ERR_NOCOLOR,
parv[0]), nick);
else
sendto_channel_butone(cptr, sptr, chptr,
(*nick == '@') ? 1 : 0,
":%s %s %s :%s",
parv[0], cmd, nick,
parv[2]);
#endif
else if (!notice)
sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN,
parv[0]), nick);
continue;
}
/*
** the following two cases allow masks in NOTICEs
** (for OPERs only)
**
** Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
*/
if ((*nick == '$' || *nick == '#') && IsAnOper(sptr))
{
if (!(s = (char *)rindex(nick, '.')))
{
sendto_one(sptr, err_str(ERR_NOTOPLEVEL,
parv[0]), nick);
continue;
}
while (*++s)
if (*s == '.' || *s == '*' || *s == '?')
break;
if (*s == '*' || *s == '?')
{
sendto_one(sptr, err_str(ERR_WILDTOPLEVEL,
parv[0]), nick);
continue;
}
sendto_match_butone(IsServer(cptr) ? cptr : NULL,
sptr, nick + 1,
(*nick == '#') ? MATCH_HOST :
MATCH_SERVER,
":%s %s %s :%s", parv[0],
cmd, nick, parv[2]);
continue;
}
/*
** nick!user@host addressed?
*/
if ((user = (char *)index(nick, '!')) &&
(host = (char *)index(nick, '@')))
{
*user = '\0';
*host = '\0';
if ((acptr = find_person(nick, NULL)) &&
!strcasecmp(user+1, acptr->user->username) &&
#ifdef RUSNET_IRCD
!strcasecmp(host+1,
(acptr->user->flags & FLAGS_VHOST) ?
acptr->user->host
: acptr->sockhost)
#else
!strcasecmp(host+1, acptr->user->host)
#endif
)
{
sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
parv[0], cmd, nick, parv[2]);
*user = '!';
*host = '@';
continue;
}
*user = '!';
*host = '@';
}
/*
** user[%host]@server addressed?
*/
if ((server = (char *)index(nick, '@')) &&
(acptr = find_server(server + 1, NULL)))
{
/*
** Not destined for a user on me :-(
*/
if (!IsMe(acptr))
{
sendto_one(acptr,":%s %s %s :%s", parv[0],
cmd, nick, parv[2]);
continue;
}
*server = '\0';
if ((host = (char *)index(nick, '%')))
*host++ = '\0';
/*
** Look for users which match the destination host
** (no host == wildcard) and if one and one only is
** found connected to me, deliver message!
*/
acptr = find_userhost(nick, host, NULL, &count);
if (server)
*server = '@';
if (host)
*--host = '%';
if (acptr)
{
if (count == 1)
sendto_prefix_one(acptr, sptr,
":%s %s %s :%s",
parv[0], cmd,
nick, parv[2]);
else if (!notice)
sendto_one(sptr, err_str(
ERR_TOOMANYTARGETS,
parv[0]), "Duplicate", nick,
"No Message Delivered");
continue;
}
}
else if ((host = (char *)index(nick, '%')))
{
/*
** user%host addressed?
*/
*host++ = '\0';
acptr = find_userhost(nick, host, NULL, &count);
*--host = '%';
if (acptr)
{
if (count == 1)
sendto_prefix_one(acptr, sptr,
":%s %s %s :%s",
parv[0], cmd,
nick, parv[2]);
else if (!notice)
sendto_one(sptr, err_str(
ERR_TOOMANYTARGETS,
parv[0]), "Duplicate", nick,
"No Message Delivered");
continue;
}
}
if (!notice)
sendto_one(sptr, err_str(ERR_NOSUCHNICK, parv[0]),
nick);
}
return penalty;
}
/*
** m_private
** parv[0] = sender prefix
** parv[1] = receiver list
** parv[2] = message text
*/
int m_private(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
return m_message(cptr, sptr, parc, parv, 0);
}
/*
** m_notice
** parv[0] = sender prefix
** parv[1] = receiver list
** parv[2] = notice text
*/
int m_notice(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
return m_message(cptr, sptr, parc, parv, 1);
}
/*
** who_one
** sends one RPL_WHOREPLY to sptr concerning acptr & repchan
*/
static void who_one(sptr, acptr, repchan, lp)
aClient *sptr, *acptr;
aChannel *repchan;
Link *lp;
{
char status[5];
int i = 0;
if (acptr->user->flags & FLAGS_AWAY)
status[i++] = 'G';
else
status[i++] = 'H';
#ifdef RUSNET_IRCD
if (acptr->user->flags & FLAGS_VHOST)
status[i++] = 'x';
#endif
if (IsAnOper(acptr))
status[i++] = '*';
if ((repchan != NULL) && (lp == NULL))
lp = find_user_link(repchan->members, acptr);
if (lp != NULL)
{
if (lp->flags & CHFL_CHANOP)
status[i++] = '@';
else if (lp->flags & CHFL_VOICE)
status[i++] = '+';
}
status[i] = '\0';
sendto_one(sptr, rpl_str(RPL_WHOREPLY, sptr->name),
(repchan) ? (repchan->chname) : "*", acptr->user->username,
#ifdef RUSNET_IRCD
(acptr->user->flags & FLAGS_VHOST) ?
#endif
acptr->user->host
#ifdef RUSNET_IRCD
: acptr->sockhost
#endif
, acptr->user->server, acptr->name,
status, acptr->hopcount, acptr->info);
}
/*
** who_channel
** lists all users on a given channel
*/
static void who_channel(sptr, chptr, oper)
aClient *sptr;
aChannel *chptr;
int oper;
{
Reg Link *lp;
int member;
if (!IsAnonymous(chptr))
{
member = IsAnOper(sptr) | IsMember(sptr, chptr);
if (member || !SecretChannel(chptr))
for (lp = chptr->members; lp; lp = lp->next)
{
if (oper && !IsAnOper(lp->value.cptr))
continue;
if (IsInvisible(lp->value.cptr) && !member)
continue;
who_one(sptr, lp->value.cptr, chptr, lp);
}
}
else if (lp = find_user_link(chptr->members, sptr))
who_one(sptr, lp->value.cptr, chptr, lp);
}
/*
** who_find
** lists all (matching) users.
** CPU intensive, but what can be done?
*/
static void who_find(sptr, mask, oper)
aClient *sptr;
char *mask;
int oper;
{
aChannel *chptr, *ch2ptr;
Link *lp;
int member;
int showperson, isinvis, isop;
aClient *acptr;
FILE *aa;
isop = IsAnOper(sptr);
for (acptr = client; acptr; acptr = acptr->next)
{
ch2ptr = NULL;
if (!IsPerson(acptr))
continue;
if (oper && !IsAnOper(acptr))
continue;
showperson = 0;
/*
* Show user if they are on the same channel, or not
* invisible and on a non secret channel (if any).
* Do this before brute force match on all relevant
* fields since these are less cpu intensive (I
* hope :-) and should provide better/more shortcuts
* -avalon
*/
isinvis = (isop) ? 0 : IsInvisible(acptr);
for (lp = acptr->user->channel; lp; lp = lp->next)
{
chptr = lp->value.chptr;
if (IsAnonymous(chptr))
continue;
member = isop | IsMember(sptr, chptr);
if (isinvis && !member)
continue;
if (member || (!isinvis && PubChannel(chptr)))
{
showperson = 1;
if (!IsAnonymous(chptr) ||
acptr != sptr)
{
ch2ptr = chptr;
break;
}
}
if (HiddenChannel(chptr) &&
!SecretChannel(chptr) && !isinvis)
showperson = 1;
}
if (!acptr->user->channel && !isinvis)
showperson = 1;
/*
** This is brute force solution, not efficient...? ;(
** Show entry, if no mask or any of the fields match
** the mask. --msa
*/
if ((isop || showperson) &&
(!mask ||
match(mask, acptr->name) == 0 ||
match(mask, acptr->user->username) == 0 ||
match(mask, acptr->user->host) == 0 ||
#ifdef RUSNET_IRCD
match(mask, acptr->sockhost) == 0 ||
#endif
match(mask, acptr->user->server) == 0 ||
match(mask, acptr->info) == 0))
who_one(sptr, acptr, ch2ptr, NULL);
}
}
/*
** m_who
** parv[0] = sender prefix
** parv[1] = nickname mask list
** parv[2] = additional selection flag, only 'o' for now.
*/
int m_who(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aChannel *chptr;
int oper = parc > 2 ? (*parv[2] == 'o' ): 0; /* Show OPERS only */
int penalty = 0;
char *p, *mask, *channame;
if (parc < 2)
{
who_find(sptr, NULL, oper);
sendto_one(sptr, rpl_str(RPL_ENDOFWHO, parv[0]), "*");
/* it was very CPU intensive */
return MAXPENALTY;
}
/* get rid of duplicates */
parv[1] = canonize(parv[1]);
for (p = NULL, mask = strtoken(&p, parv[1], ",");
mask && penalty <= MAXPENALTY;
mask = strtoken(&p, NULL, ","))
{
channame = NULL;
penalty += 1;
/* find channel user last joined, we might need it later */
if (sptr->user && sptr->user->channel)
channame = sptr->user->channel->value.chptr->chname;
#if 0
/* I think it's useless --Beeth */
clean_channelname(mask);
#endif
/* simplify mask */
(void)collapse(mask);
/*
** We can never have here !mask
** or *mask == '\0', since it would be equal
** to parc == 1, that is 'WHO' and/or would not
** pass through above for loop.
*/
if (mask[1] == '\0' && mask[0] == '0')
{
/*
** 'WHO 0' - do who_find() later
*/
mask = NULL;
channame = NULL;
}
else if (mask[1] == '\0' && mask[0] == '*')
{
/*
** 'WHO *'
** If user was on any channel, list the one
** he joined last.
*/
mask = NULL;
}
else
{
/*
** Try if mask was channelname and if yes, do
** who_channel, else if mask was nick, do who_one.
** Else do horrible who_find()
*/
channame = mask;
}
if (IsChannelName(channame))
{
chptr = find_channel(channame, NULL);
if (chptr)
{
who_channel(sptr, chptr, oper);
}
else
{
/*
** 'WHO #nonexistant'.
*/
penalty += 1;
}
}
else
{
aClient *acptr = NULL;
if (mask)
{
/*
** Here mask can be NULL. It doesn't matter,
** since find_client would return NULL.
** Just saving one function call. ;)
*/
acptr = find_client(mask, NULL);
if (acptr && !IsClient(acptr))
{
acptr = NULL;
}
}
if (acptr)
{
/* We found client, so send WHO for it */
who_one(sptr, acptr, NULL, NULL);
}
else
{
/*
** All nice chances lost above.
** We must hog our server with that.
*/
who_find(sptr, mask, oper);
penalty += MAXPENALTY;
}
}
sendto_one(sptr, rpl_str(RPL_ENDOFWHO, parv[0]),
BadPtr(mask) ? "*" : mask);
}
return penalty;
}
/* send_whois() is used by m_whois() to send whois reply to sptr, for acptr */
static void
send_whois(sptr, acptr, parc, parv)
aClient *sptr, *acptr;
int parc;
char *parv[];
{
static anUser UnknownUser =
{
NULL, /* channel */
NULL, /* invited */
NULL, /* uwas */
NULL, /* away */
0, /* last */
1, /* refcount */
0, /* joined */
0, /* flags */
NULL, /* servp */
NULL, /* next, prev, bcptr */
"<Unknown>", /* user */
"<Unknown>", /* host */
"<Unknown>", /* server */
};
Link *lp;
anUser *user;
aChannel *chptr;
aClient *a2cptr;
int len, mlen;
char *name;
user = acptr->user ? acptr->user : &UnknownUser;
name = (!*acptr->name) ? "?" : acptr->name;
a2cptr = find_server(user->server, NULL);
#ifdef RUSNET_IRCD
if (acptr->user && acptr->user->flags & FLAGS_VHOST)
{
sendto_one(sptr, rpl_str(RPL_WHOISUSER, sptr->name), name,
user->username, user->host, acptr->info);
if (IsOper(sptr))
sendto_one(sptr, rpl_str(RPL_WHOISHOST, sptr->name),
acptr->name, acptr->sockhost);
}
else
sendto_one(sptr, rpl_str(RPL_WHOISUSER, sptr->name), name,
user->username, acptr->sockhost, acptr->info);
#else
sendto_one(sptr, rpl_str(RPL_WHOISUSER, sptr->name), name,
user->username, user->host, acptr->info);
#endif
mlen = strlen(ME) + strlen(sptr->name) + 6 + strlen(name);
*buf = '\0';
/* Mark IRC Services from being at any channel */
#if defined( RUSNET_IRCD ) && defined( USE_SERVICES )
if (strcasecmp(user->host, SERVICES_HOST))
#endif
for (len = 0, lp = user->channel; lp; lp = lp->next)
{
chptr = lp->value.chptr;
if ((!IsAnonymous(chptr) || acptr == sptr) &&
ShowChannel(sptr, chptr) || IsAnOper(sptr))
{
if (len + strlen(chptr->chname)
> (size_t) BUFSIZE - 4 - mlen)
{
sendto_one(sptr, ":%s %d %s %s :%s", ME,
RPL_WHOISCHANNELS, sptr->name, name,
buf);
*buf = '\0';
len = 0;
}
if (is_chan_op(acptr, chptr))
*(buf + len++) = '@';
else if (has_voice(acptr, chptr))
*(buf + len++) = '+';
if (len)
*(buf + len) = '\0';
(void)strcpy(buf + len, chptr->chname);
len += strlen(chptr->chname);
(void)strcat(buf + len, " ");
len++;
}
}
if (buf[0] != '\0')
sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS, sptr->name), name,
buf);
sendto_one(sptr, rpl_str(RPL_WHOISSERVER, sptr->name),
name, user->server,
a2cptr ? a2cptr->info:"*Not On This Net*");
if (user->flags & FLAGS_AWAY)
sendto_one(sptr, rpl_str(RPL_AWAY, sptr->name), name,
(user->away) ? user->away : "Gone");
#ifdef RUSNET_IRCD
if (MyConnect(acptr) && acptr->user && acptr->transptr &&
acptr->transptr->id)
sendto_one(sptr, rpl_str(RPL_CHARSET, sptr->name),
acptr->name, acptr->transptr->id);
#endif
if (IsAnOper(acptr))
sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR, sptr->name), name);
#if defined(EXTRA_NOTICES) && defined(WHOIS_NOTICES)
if (IsAnOper(acptr) && acptr != sptr)
sendto_one(acptr, ":%s NOTICE %s :WHOIS on YOU requested by %s "
"(%s@%s) [%s]", ME, acptr->name, parv[0],
sptr->user->username,
#ifdef RUSNET_IRCD
sptr->sockhost,
#else
sptr->user->host,
#endif
sptr->user->server);
#endif
if (acptr->user && MyConnect(acptr))
sendto_one(sptr, rpl_str(RPL_WHOISIDLE, sptr->name),
name, timeofday - user->last, acptr->firsttime);
}
/*
** m_whois
** parv[0] = sender prefix
** parv[1] = nickname masklist
*/
int m_whois(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Link *lp;
aClient *acptr;
aChannel *chptr;
char *nick, *tmp, *tmp2;
char *p = NULL;
int isop, found = 0;
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN, parv[0]));
return 1;
}
if (parc > 2)
{
if (hunt_server(cptr,sptr,":%s WHOIS %s :%s", 1,parc,parv) !=
HUNTED_ISME)
return 3;
parv[1] = parv[2];
}
isop = IsAnOper(sptr);
tmp = mystrdup(parv[1]);
for (tmp2 = canonize(tmp); (nick = strtoken(&p, tmp2, ","));
tmp2 = NULL)
{
int invis, showperson, member, wilds;
found &= 0x0f; /* high/boolean, low/counter */
(void)collapse(nick);
wilds = (index(nick, '?') || index(nick, '*'));
/*
* We're no longer allowing remote users to generate
* requests with wildcard, nor local users with more
* than one wildcard target per command.
* Max 3 targets per command allowed.
*/
if ((wilds && (!MyConnect(sptr) || p)) || found++ > 3)
break;
if (!wilds)
{
acptr = hash_find_client(nick, (aClient *)NULL);
if (!acptr || !IsPerson(acptr))
sendto_one(sptr,
err_str(ERR_NOSUCHNICK, parv[0]),
nick);
else
send_whois(sptr, acptr, parc, parv);
continue;
}
for (acptr = client; (acptr = next_client(acptr, nick));
acptr = acptr->next)
{
if (IsServer(acptr) || IsService(acptr))
continue;
/*
* I'm always last :-) and acptr->next == NULL!!
*/
if (IsMe(acptr))
break;
/*
* 'Rules' established for sending a WHOIS reply:
* - if wildcards are being used don't send a reply if
* the querier isnt any common channels and the
* client in question is invisible and wildcards are
* in use (allow exact matches only);
* - only send replies about common or public channels
* the target user(s) are on;
*/
invis = (isop) ? 0 : (acptr->user) ?
(acptr->user->flags & FLAGS_INVISIBLE) : 0;
member = (isop ||
acptr->user && acptr->user->channel) ? 1 : 0;
showperson = (wilds && !invis && !member) || !wilds;
for (lp = (acptr->user) ? acptr->user->channel : NULL;
lp; lp = lp->next)
{
chptr = lp->value.chptr;
if (IsAnOper(sptr))
{
showperson = 1;
break;
}
if (IsAnonymous(chptr))
continue;
member = IsMember(sptr, chptr);
if (invis && !member)
continue;
if (member || (!invis && PubChannel(chptr)))
{
showperson = 1;
break;
}
if (!invis && HiddenChannel(chptr) &&
!SecretChannel(chptr))
showperson = 1;
}
if (!showperson)
continue;
found |= 0x10;
send_whois(sptr, acptr, parc, parv);
}
if (!(found & 0x10))
{
if (strlen(nick) > (size_t) NICKLEN)
nick[NICKLEN] = '\0';
sendto_one(sptr, err_str(ERR_NOSUCHNICK, parv[0]),
nick);
}
if (p)
p[-1] = ',';
}
sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS, parv[0]), parv[1]);
MyFree(tmp);
return 2;
}
/*
** m_user
** parv[0] = sender prefix
** parv[1] = username (login name, account)
** parv[2] = client host name (used only from other servers)
** parv[3] = server host name (used only from other servers)
** parv[4] = users real name info
** parv[5] = users mode (is only used internally by the server,
** NULL otherwise)
*/
int m_user(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
#ifdef RUSNET_IRCD
#define UFLAGS (FLAGS_INVISIBLE|FLAGS_WALLOP|FLAGS_VHOST)
#else
#define UFLAGS (FLAGS_INVISIBLE|FLAGS_WALLOP)
#endif
char *username, *host, *server, *realname;
anUser *user;
/* Reject new USER */
if (IsServer(sptr) || IsService(sptr) || sptr->user)
{
sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
return 1;
}
if (parc > 2 && (username = (char *)index(parv[1],'@')))
*username = '\0';
if (parc < 5 || *parv[1] == '\0' || *parv[2] == '\0' ||
*parv[3] == '\0' || *parv[4] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "USER");
if (IsServer(cptr))
{
/* send error */
sendto_flag(SCH_NOTICE,
"bad USER param count for %s from %s",
parv[0], get_client_name(cptr, FALSE));
/*
** and kill it, as there's no reason to expect more
** USER messages about it, or we'll create a ghost.
*/
sendto_one(cptr,
":%s KILL %s :%s (bad USER param count)",
ME, parv[0], ME);
sptr->flags |= FLAGS_KILLED;
exit_client(NULL, sptr, &me, "bad USER param count");
}
return 1;
}
/* Copy parameters into better documenting variables */
username = (parc < 2 || BadPtr(parv[1])) ? "<bad-boy>" : parv[1];
host = (parc < 3 || BadPtr(parv[2])) ? "<nohost>" : parv[2];
server = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3];
realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
user = make_user(sptr);
if (MyConnect(sptr))
{
user->servp = me.serv;
me.serv->refcnt++;
#ifndef NO_DEFAULT_INVISIBLE
SetInvisible(sptr);
#endif
#ifndef NO_DEFAULT_VHOST
SetVHost(sptr);
#endif
if (sptr->flags & FLAGS_RILINE)
sptr->user->flags |= FLAGS_RESTRICTED;
sptr->user->flags |= (UFLAGS & atoi(host));
user->server = find_server_string(me.serv->snum);
}
else
{
aClient *acptr = NULL;
aServer *sp = NULL;
if (!(sp = find_tokserver(atoi(server), cptr, NULL)))
{
/*
** Why? Why do we keep doing this?
** s_service.c had the same kind of kludge.
** Can't we get rid of this? - krys
*/
acptr = find_server(server, NULL);
if (acptr)
sendto_flag(SCH_ERROR,
"ERROR: SERVER:%s uses wrong syntax for NICK (%s)",
get_client_name(cptr, FALSE),
parv[0]);
}
if (acptr)
sp = acptr->serv;
else if (!sp)
{
sendto_flag(SCH_ERROR,
"ERROR: USER:%s without SERVER:%s from %s",
parv[0], server,
get_client_name(cptr, FALSE));
ircstp->is_nosrv++;
return exit_client(NULL, sptr, &me, "No Such Server");
}
user->servp = sp;
user->servp->refcnt++;
Debug((DEBUG_DEBUG, "from %s user %s server %s -> %#x %s",
parv[0], username, server, sp, sp->bcptr->name));
user->server = find_server_string(sp->snum);
}
strncpyzt(user->host, host, sizeof(user->host));
reorder_client_in_list(sptr);
if (sptr->info != DefInfo)
MyFree(sptr->info);
if (strlen(realname) > REALLEN)
realname[REALLEN] = '\0';
sptr->info = mystrdup(realname);
if (sptr->name[0]) /* NICK already received, now we have USER... */
{
if ((parc == 6) && IsServer(cptr)) /* internal m_user() */
{
char *pv[4];
pv[0] = ME;
pv[1] = sptr->name;
pv[2] = parv[5];
pv[3] = NULL;
m_umode(NULL, sptr, 3, pv);/*internal fake call again*/
/* The internal m_umode does NOT propagate to 2.8
** servers. (it can NOT since NICK/USER hasn't been
** sent yet). See register_user()
*/
}
return register_user(cptr, sptr, sptr->name, username);
}
else
strncpyzt(sptr->user->username, username, USERLEN+1);
return 2;
}
/*
** m_quit
** parv[0] = sender prefix
** parv[1] = comment
*/
int m_quit(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
static char quitc[] = "I Quit";
char *newcomment;
int newlen, result;
register char *comment = (parc > 1 && parv[1]) ? parv[1] : quitc;
if (IsServer(sptr))
return 0;
else {
if (MyClient(sptr) || MyService(sptr))
if (!strncmp("Local Kill", comment, 10) ||
!strncmp(comment, "Killed", 6))
comment = quitc;
if (strlen(comment) > (size_t) (TOPICLEN - 2))
comment[TOPICLEN - 2] = '\0';
if (!MyClient(sptr))
return exit_client(cptr, sptr, sptr, comment);
else {
newlen = strlen(comment);
newcomment = MyMalloc(newlen + 3);
*newcomment = '"';
strcpy(newcomment + 1, comment);
newcomment[newlen + 1] = '"';
newcomment[newlen + 2] = '\0';
result = exit_client(cptr, sptr, sptr, newcomment);
MyFree(newcomment);
return result;
}
}
}
/*
** m_kill
** parv[0] = sender prefix
** parv[1] = kill victim
** parv[2] = kill path
*/
int m_kill(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aClient *acptr;
char *inpath = get_client_name(cptr,FALSE);
char *user, *path, *killer;
int chasing = 0;
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "KILL");
return 1;
}
user = parv[1];
path = parv[2]; /* Either defined or NULL (parc >= 2!!) */
if (IsAnOper(cptr))
{
if (BadPtr(path))
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
"KILL");
return 1;
}
if (strlen(path) > (size_t) TOPICLEN)
path[TOPICLEN] = '\0';
}
if (!(acptr = find_client(user, NULL)))
{
/*
** If the user has recently changed nick, we automaticly
** rewrite the KILL for this new nickname--this keeps
** servers in synch when nick change and kill collide
*/
if (!(acptr = get_history(user, (long)KILLCHASETIMELIMIT)))
{
if (!IsServer(sptr))
sendto_one(sptr, err_str(ERR_NOSUCHNICK,
parv[0]), user);
return 1;
}
/*
** Just trying to keep away from kill collision, lock the
** nick kill message was recieved for with lock_nick(nick)
** -kmale
*/
#ifdef LOCKRECENTCHANGE
lock_nick(user, sptr->name);
#endif
sendto_one(sptr,":%s NOTICE %s :KILL changed from %s to %s",
ME, parv[0], user, acptr->name);
chasing = 1;
}
if (!MyConnect(acptr) && IsLocOp(cptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
return 1;
}
if (IsServer(acptr) || IsMe(acptr))
{
sendto_flag(SCH_ERROR, "%s tried to KILL server %s: %s %s %s",
sptr->name, acptr->name, parv[0], parv[1], parv[2]);
sendto_one(sptr, err_str(ERR_CANTKILLSERVER, parv[0]),
acptr->name);
return 1;
}
#ifdef LOCAL_KILL_ONLY
if (MyOper(sptr) && !MyConnect(acptr))
{
sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
ME, parv[0], acptr->name);
return 1;
}
#endif
if (!IsServer(cptr))
{
/*
** The kill originates from this server, initialize path.
** (In which case the 'path' may contain user suplied
** explanation ...or some nasty comment, sigh... >;-)
**
** ...!operhost!oper
** ...!operhost!oper (comment)
*/
if (IsUnixSocket(cptr)) /* Don't use get_client_name syntax */
inpath = me.sockhost;
else
inpath = cptr->sockhost;
if (!BadPtr(path))
{
SPRINTF(buf, "%s%s (%s)",
cptr->name, IsOper(sptr) ? "" : "(L)", path);
path = buf;
}
else
path = cptr->name;
}
else if (BadPtr(path))
path = "*no-path*"; /* Bogus server sending??? */
/*
** Notify all *local* opers about the KILL (this includes the one
** originating the kill, if from this server--the special numeric
** reply message is not generated anymore).
**
** Note: "acptr->name" is used instead of "user" because we may
** have changed the target because of the nickname change.
*/
if (IsLocOp(sptr) && !MyConnect(acptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
return 1;
}
sendto_flag(SCH_KILL,
"Received KILL message for %s (%s@%s)%s. From %s Path: %s!%s",
acptr->name, (acptr->user) ? acptr->user->username : "unknown",
acptr->sockhost, (acptr->user) ? acptr->user->server : "unknown",
parv[0], inpath, path);
#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
if (IsOper(sptr))
syslog(LOG_DEBUG,"KILL From %s For %s (%s@%s)%s Path %s!%s",
parv[0], acptr->name,
(acptr->user) ? acptr->user->username : "unknown",
acptr->sockhost, (acptr->user) ? acptr->user->server : "unknown",
inpath, path);
#endif
/*
** And pass on the message to other servers. Note, that if KILL
** was changed, the message has to be sent to all links, also
** back.
** Suicide kills are NOT passed on --SRB
*/
if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
{
sendto_serv_butone(cptr, ":%s KILL %s :%s!%s",
parv[0], acptr->name, inpath, path);
if (chasing && !IsClient(cptr))
sendto_one(cptr, ":%s KILL %s :%s!%s",
ME, acptr->name, inpath, path);
acptr->flags |= FLAGS_KILLED;
}
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_KILL, NULL, sptr,
":%s KILL %s :%s!%s", parv[0], acptr->name,
inpath, path);
#endif
/*
** Tell the victim she/he has been zapped, but *only* if
** the victim is on current server--no sense in sending the
** notification chasing the above kill, it won't get far
** anyway (as this user don't exist there any more either)
*/
if (MyConnect(acptr))
sendto_prefix_one(acptr, sptr,":%s KILL %s :%s!%s",
parv[0], acptr->name, inpath, path);
/*
** Set FLAGS_KILLED. This prevents exit_one_client from sending
** the unnecessary QUIT for this. (This flag should never be
** set in any other place)
*/
if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
{
acptr->exitc = EXITC_KILL;
SPRINTF(buf2, "Local Kill by %s (%s)", sptr->name,
BadPtr(parv[2]) ? sptr->name : parv[2]);
}
else
{
if ((killer = index(path, ' ')))
{
while (killer > path && *killer != '!')
killer--;
if (killer != path)
killer++;
}
else
killer = path;
SPRINTF(buf2, "Killed (%s)", killer);
}
return exit_client(cptr, acptr, sptr, buf2);
}
/***********************************************************************
* m_away() - Added 14 Dec 1988 by jto.
* Not currently really working, I don't like this
* call at all...
*
* ...trying to make it work. I don't like it either,
* but perhaps it's worth the load it causes to net.
* This requires flooding of the whole net like NICK,
* USER, MODE, etc messages... --msa
***********************************************************************/
/*
** m_away
** parv[0] = sender prefix
** parv[1] = away message
*/
int m_away(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg char *away, *awy2 = parv[1];
int len;
away = sptr->user->away;
if (parc < 2 || !*awy2) /* Marking as not away */
{
if (away)
{
istat.is_away--;
istat.is_awaymem -= (strlen(away) + 1);
MyFree(away);
sptr->user->away = NULL;
}
if (sptr->user->flags & FLAGS_AWAY)
sendto_serv_butone(cptr, ":%s MODE %s :-a", parv[0],
parv[0]);
/* sendto_serv_butone(cptr, ":%s AWAY", parv[0]); */
if (MyConnect(sptr))
sendto_one(sptr, rpl_str(RPL_UNAWAY, parv[0]));
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_AWAY, NULL, sptr,
":%s AWAY", parv[0]);
#endif
sptr->user->flags &= ~FLAGS_AWAY;
return 1;
}
/* Marking as away */
if ((len = strlen(awy2)) > (size_t) TOPICLEN)
{
len = TOPICLEN;
awy2[TOPICLEN] = '\0';
}
len++;
/* sendto_serv_butone(cptr, ":%s AWAY :%s", parv[0], awy2); */
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_AWAY, NULL, sptr,
":%s AWAY :%s", parv[0], awy2);
#endif
if (away)
{
istat.is_awaymem -= (strlen(away) + 1);
away = (char *)MyRealloc(away, len);
istat.is_awaymem += len;
}
else
{
istat.is_away++;
istat.is_awaymem += len;
away = (char *)MyMalloc(len);
sendto_serv_butone(cptr, ":%s MODE %s :+a", parv[0], parv[0]);
}
sptr->user->flags |= FLAGS_AWAY;
if (MyConnect(sptr))
{
sptr->user->away = away;
(void)strcpy(away, awy2);
sendto_one(sptr, rpl_str(RPL_NOWAWAY, parv[0]));
}
return 2;
}
/*
** m_ping
** parv[0] = sender prefix
** parv[1] = origin
** parv[2] = destination
*/
int m_ping(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aClient *acptr;
char *origin, *destination;
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NOORIGIN, parv[0]));
return 1;
}
origin = parv[1];
destination = parv[2]; /* Will get NULL or pointer (parc >= 2!!) */
acptr = find_client(origin, NULL);
if (!acptr)
acptr = find_server(origin, NULL);
if (!acptr || acptr != sptr)
origin = cptr->name;
if (!BadPtr(destination) && match(destination, ME) != 0)
{
if ((acptr = find_server(destination, NULL)))
sendto_one(acptr,":%s PING %s :%s", parv[0],
origin, destination);
else
{
sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
destination);
return 1;
}
}
else
sendto_one(sptr, ":%s PONG %s :%s", ME,
(destination) ? destination : ME, origin);
return 1;
}
/*
** m_pong
** parv[0] = sender prefix
** parv[1] = origin
** parv[2] = destination
*/
int m_pong(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aClient *acptr;
char *origin, *destination;
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NOORIGIN, parv[0]));
return 1;
}
origin = parv[1];
destination = parv[2];
cptr->flags &= ~FLAGS_PINGSENT;
sptr->flags &= ~FLAGS_PINGSENT;
if (!BadPtr(destination) && strcasecmp(destination, ME) != 0)
{
if ((acptr = find_client(destination, NULL)) ||
(acptr = find_server(destination, NULL))) {
if (!(MyClient(sptr) && strcasecmp(origin, sptr->name)))
sendto_one(acptr,":%s PONG %s %s",
parv[0], origin, destination);
} else
sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
destination);
return 2;
}
#ifdef DEBUGMODE
else
Debug((DEBUG_NOTICE, "PONG: %s %s", origin,
destination ? destination : "*"));
#endif
return 1;
}
/*
** m_oper
** parv[0] = sender prefix
** parv[1] = oper name
** parv[2] = oper password
*/
int m_oper(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aConfItem *aconf;
char *name, *password, *encr;
name = parc > 1 ? parv[1] : NULL;
password = parc > 2 ? parv[2] : NULL;
if (!IsServer(cptr) && (BadPtr(name) || BadPtr(password)))
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "OPER");
return 1;
}
/* if message arrived from server, trust it, and set to oper */
if ((IsServer(cptr) || IsMe(cptr)) && !IsOper(sptr))
{
/* we never get here, do we?? (counters!) -krys */
sptr->user->flags |= FLAGS_OPER;
sendto_serv_butone(cptr, ":%s MODE %s :+o", parv[0], parv[0]);
if (IsMe(cptr))
sendto_one(sptr, rpl_str(RPL_YOUREOPER, parv[0]));
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
sptr, ":%s MODE %s :+o", parv[0],
parv[0]);
#endif
return 1;
}
else if (IsAnOper(sptr))
{
if (MyConnect(sptr))
sendto_one(sptr, rpl_str(RPL_YOUREOPER, parv[0]));
return 1;
}
if (!(aconf = find_Oline(name, sptr)))
{
#ifdef EXTRA_NOTICES
sendto_flag(SCH_OPER, "No O:line for the name %s [%s!%s@%s] from %s(%s)",
parv[1], sptr->name, sptr->user->username,
#ifdef RUSNET_IRCD
(sptr->user->flags & FLAGS_VHOST) ?
#endif /* RUSNET_IRCD */
sptr->user->host
#ifdef RUSNET_IRCD
: sptr->sockhost
#endif /* RUSNET_IRCD */
, get_client_name(cptr, TRUE), sptr->user->server);
#endif /* EXTRA_NOTICES */
sendto_one(sptr, err_str(ERR_NOOPERHOST, parv[0]));
return 1;
}
#ifdef CRYPT_OPER_PASSWORD
/* pass whole aconf->passwd as salt, let crypt() deal with it */
if (password && aconf->passwd)
{
extern char *crypt();
encr = crypt(password, aconf->passwd);
if (encr == NULL)
{
sendto_flag(SCH_ERROR, "crypt() returned NULL");
#ifdef EXTRA_NOTICES
sendto_flag(SCH_OPER, "crypt() failed on O:line for the name %s \
[%s!%s@%s] from %s(%s)",
parv[1], sptr->name, sptr->user->username,
#ifdef RUSNET_IRCD
(sptr->user->flags & FLAGS_VHOST) ?
#endif /* RUSNET_IRCD */
sptr->user->host
#ifdef RUSNET_IRCD
: sptr->sockhost
#endif /* RUSNET_IRCD */
, get_client_name(cptr, TRUE), sptr->user->server);
#endif /* EXTRA_NOTICES */
sendto_one(sptr,err_str(ERR_PASSWDMISMATCH, parv[0]));
return 3;
}
}
else
encr = "";
#else
encr = password;
#endif /* CRYPT_OPER_PASSWORD */
if ((aconf->status & CONF_OPS) &&
StrEq(encr, aconf->passwd) && !attach_conf(sptr, aconf))
{
int old = (sptr->user->flags & ALL_UMODES);
char *s;
s = index(aconf->host, '@');
*s++ = '\0';
#ifdef OPER_REMOTE
if (aconf->status == CONF_LOCOP)
#else
if ((match(s,me.sockhost) && !IsLocal(sptr)) ||
aconf->status == CONF_LOCOP)
#endif
SetLocOp(sptr);
else
SetOper(sptr);
*--s = '@';
sendto_flag(SCH_NOTICE, "%s (%s@%s) is now operator (%c)",
parv[0], sptr->user->username,
#ifdef RUSNET_IRCD
(sptr->user->flags & FLAGS_VHOST) ?
#endif /* RUSNET_IRCD */
sptr->user->host
#ifdef RUSNET_IRCD
: sptr->sockhost
#endif /* RUSNET_IRCD */
, IsOper(sptr) ? 'O' : 'o');
send_umode_out(cptr, sptr, old);
sendto_one(sptr, rpl_str(RPL_YOUREOPER, parv[0]));
#if !defined(CRYPT_OPER_PASSWORD) && (defined(FNAME_OPERLOG) ||\
(defined(USE_SYSLOG) && defined(SYSLOG_OPER)))
encr = "";
#endif
#if defined(USE_SYSLOG) && defined(SYSLOG_OPER)
syslog(LOG_INFO, "OPER (%s) (%s) by (%s!%s@%s) [%s@%s]",
name, encr,
parv[0], sptr->user->username, sptr->user->host,
sptr->auth, IsUnixSocket(sptr) ? sptr->sockhost :
#ifdef INET6
inet_ntop(AF_INET6, (char *)&sptr->ip,
mydummy, MYDUMMY_SIZE)
#else
inetntoa((char *)&sptr->ip)
#endif /* INET6 */
);
#endif
#ifdef FNAME_OPERLOG
{
int logfile;
/*
* This conditional makes the logfile active only after
* it's been created - thus logging can be turned off by
* removing the file.
*
* stop NFS hangs...most systems should be able to open a
* file in 3 seconds. -avalon (curtesy of wumpus)
*/
(void)alarm(3);
if (IsPerson(sptr) &&
(logfile = open(LOG_DIR "/" FNAME_OPERLOG, O_WRONLY|O_APPEND)) != -1)
{
(void)alarm(0);
SPRINTF(buf, "%s OPER (%s) (%s) by (%s!%s@%s) [%s@%s]\n",
myctime(timeofday), name, encr,
parv[0], sptr->user->username, sptr->user->host,
sptr->auth, IsUnixSocket(sptr) ? sptr->sockhost :
#ifdef INET6
inetntop(AF_INET6, (char *)&sptr->ip, mydummy,
MYDUMMY_SIZE));
#else
inetntoa((char *)&sptr->ip));
#endif
(void)alarm(3);
(void)write(logfile, buf, strlen(buf));
(void)alarm(0);
(void)close(logfile);
}
(void)alarm(0);
/* Modification by pjg */
}
#endif
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
sptr, ":%s MODE %s :+%c", parv[0],
parv[0], IsOper(sptr) ? 'O' : 'o');
#endif
if (IsAnOper(sptr))
istat.is_oper++;
}
else
{
(void)detach_conf(sptr, aconf);
#ifdef EXTRA_NOTICES
sendto_flag(SCH_OPER, "Incorrect OPER password for the name %s \
[%s!%s@%s] from %s(%s)",
parv[1], sptr->name, sptr->user->username,
#ifdef RUSNET_IRCD
(sptr->user->flags & FLAGS_VHOST) ?
#endif /* RUSNET_IRCD */
sptr->user->host
#ifdef RUSNET_IRCD
: sptr->sockhost
#endif /* RUSNET_IRCD */
, get_client_name(cptr, TRUE), sptr->user->server);
#endif /* EXTRA_NOTICES */
sendto_one(sptr,err_str(ERR_PASSWDMISMATCH, parv[0]));
}
return 3;
}
/***************************************************************************
* m_pass() - Added Sat, 4 March 1989
***************************************************************************/
/*
** m_pass
** parv[0] = sender prefix
** parv[1] = password
** parv[2] = protocol & server versions (server only)
** parv[3] = server id & options (server only)
** parv[4] = (optional) link options (server only)
*/
int m_pass(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
char *password = parc > 1 ? parv[1] : NULL;
if (BadPtr(password))
{
sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "PASS");
return 1;
}
/* Temporarily store PASS pwd *parameters* into info field */
if (parc > 2 && parv[2])
{
strncpyzt(buf, parv[2], 15);
if (parc > 3 && parv[3])
{
strcat(buf, " ");
strncat(buf, parv[3], 100);
if (parc > 4 && parv[4])
{
strcat(buf, " ");
strncat(buf, parv[4], 5);
}
}
if (cptr->info != DefInfo)
MyFree(cptr->info);
cptr->info = mystrdup(buf);
}
strncpyzt(cptr->passwd, password, sizeof(cptr->passwd));
return 1;
}
/*
* m_userhost added by Darren Reed 13/8/91 to aid clients and reduce
* the need for complicated requests like WHOIS. It returns user/host
* information only (no spurious AWAY labels or channels).
*/
int m_userhost(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
char *p = NULL;
aClient *acptr;
Reg char *s;
Reg int i, len;
int idx = 1;
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
"USERHOST");
return 1;
}
(void)strcpy(buf, rpl_str(RPL_USERHOST, parv[0]));
len = strlen(buf);
*buf2 = '\0';
for (i = 5, s = strtoken(&p, parv[idx], " "); i && s; i--)
{
if ((acptr = find_person(s, NULL)))
{
if (*buf2)
(void)strcat(buf, " ");
SPRINTF(buf2, "%s%s=%c%s@%s", acptr->name,
IsAnOper(acptr) ? "*" : "",
(acptr->user->flags & FLAGS_AWAY) ? '-' : '+',
acptr->user->username,
#ifdef RUSNET_IRCD
(acptr->user->flags & FLAGS_VHOST) ?
#endif
acptr->user->host
#ifdef RUSNET_IRCD
: acptr->sockhost
#endif
);
(void)strncat(buf, buf2, sizeof(buf) - len);
len += strlen(buf2);
if (len > BUFSIZE - (NICKLEN + 5 + HOSTLEN + USERLEN))
{
sendto_one(sptr, "%s", buf);
(void)strcpy(buf, rpl_str(RPL_USERHOST,
parv[0]));
len = strlen(buf);
*buf2 = '\0';
}
}
s = strtoken(&p, (char *)NULL, " ");
if (!s && parv[++idx])
{
p = NULL;
s = strtoken(&p, parv[idx], " ");
}
}
sendto_one(sptr, "%s", buf);
return 1;
}
/*
* m_ison added by Darren Reed 13/8/91 to act as an efficent user indicator
* with respect to cpu/bandwidth used. Implemented for NOTIFY feature in
* clients. Designed to reduce number of whois requests. Can process
* nicknames in batches as long as the maximum buffer length.
*
* format:
* ISON :nicklist
*/
int m_ison(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg aClient *acptr;
Reg char *s, **pav = parv;
Reg int len = 0, i;
char *p = NULL;
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "ISON");
return 1;
}
(void)strcpy(buf, rpl_str(RPL_ISON, *parv));
len = strlen(buf);
for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " "))
if ((acptr = find_person(s, NULL)))
{
i = strlen(acptr->name);
if (len + i > sizeof(buf) - 4)
{
/* leave room for " \r\n\0" */
break;
}
(void) strcpy(buf + len, acptr->name);
len += i;
(void) strcpy(buf + len++, " ");
}
sendto_one(sptr, "%s", buf);
return 1;
}
/*
* m_umode() added 15/10/91 By Darren Reed.
* parv[0] - sender (can be NULL, see below..)
* parv[1] - username to change mode for
* parv[2] - modes to change
*/
int m_umode(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg int flag;
Reg int *s;
Reg char **p, *m;
aClient *acptr = NULL;
int what, setflags, penalty = 0;
what = MODE_ADD;
if (parc < 2)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "MODE");
return 1;
}
if (cptr && !(acptr = find_person(parv[1], NULL)))
{
if (MyConnect(sptr))
sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL, parv[0]),
parv[1]);
return 1;
}
if (cptr == NULL)
/* internal call which has to be handled in a special way */
acptr = sptr;
if ((cptr != NULL) &&
((IsServer(sptr) || sptr != acptr || acptr->from != sptr->from)))
{
if (IsServer(cptr))
sendto_flag(SCH_ERROR,
"%s MODE for User %s From %s!%s",
ME, parv[1],
get_client_name(cptr, FALSE), sptr->name);
else
sendto_one(sptr, err_str(ERR_USERSDONTMATCH, parv[0]));
return 1;
}
if (parc < 3)
{
m = buf;
*m++ = '+';
for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4);
s += 2)
if (sptr->user->flags & flag)
*m++ = (char)(*(s+1));
*m = '\0';
sendto_one(sptr, rpl_str(RPL_UMODEIS, parv[0]), buf);
return 1;
}
/* find flags already set for user */
setflags = 0;
for (s = user_modes; (flag = *s); s += 2)
if (sptr->user->flags & flag)
setflags |= flag;
/*
* parse mode change string(s)
*/
for (p = &parv[2]; p && *p; p++ )
for (m = *p; *m; m++)
switch(*m)
{
case '+' :
what = MODE_ADD;
break;
case '-' :
what = MODE_DEL;
break;
/* we may not get these,
* but they shouldnt be in default
*/
case ' ' :
case '\n' :
case '\r' :
case '\t' :
break;
case 'a' : /* fall through case */
/* users should use the AWAY message */
if (cptr && !IsServer(cptr))
break;
if (what == MODE_DEL && sptr->user->away)
{
istat.is_away--;
istat.is_awaymem -= (strlen(sptr->user->away) + 1);
MyFree(sptr->user->away);
sptr->user->away = NULL;
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_AWAY,
sptr->user->server, sptr,
":%s AWAY", parv[0]);
#endif
}
#ifdef USE_SERVICES
if (what == MODE_ADD)
check_services_butone(SERVICE_WANT_AWAY,
sptr->user->server, sptr,
":%s AWAY :", parv[0]);
#endif
default :
for (s = user_modes; (flag = *s); s += 2)
if (*m == (char)(*(s + 1)))
{
if (what == MODE_ADD)
sptr->user->flags |= flag;
else
sptr->user->flags &= ~flag;
penalty += 1;
break;
}
if (flag == 0 && MyConnect(sptr))
sendto_one(sptr, err_str(
ERR_UMODEUNKNOWNFLAG, parv[0]),
*m);
break;
}
/*
* stop users making themselves operators too easily
*/
if (cptr)
{
if (!(setflags & FLAGS_OPER) && IsOper(sptr) &&
!IsServer(cptr))
ClearOper(sptr);
#ifdef NO_DIRECT_VHOST
if (!(setflags & FLAGS_VHOST) && HasVHost(sptr) &&
!(IsServer(cptr) || IsMe(cptr) || IsOper(sptr)))
ClearVHost(sptr);
#endif
if (!(setflags & FLAGS_LOCOP) && IsLocOp(sptr))
sptr->user->flags &= ~FLAGS_LOCOP;
if ((setflags & FLAGS_RESTRICTED) &&
!(IsServer(cptr) || IsMe(cptr)) &&
!(sptr->user->flags & FLAGS_RESTRICTED))
{
sendto_one(sptr, err_str(ERR_RESTRICTED, parv[0]));
SetRestricted(sptr);
/* Can't return; here since it could mess counters */
}
if ((setflags & (FLAGS_OPER|FLAGS_LOCOP)) && !IsAnOper(sptr) &&
MyConnect(sptr))
det_confs_butmask(sptr, CONF_CLIENT);
/*
* compare new flags with old flags and send string which
* will cause servers to update correctly.
*/
if (!IsInvisible(sptr) && (setflags & FLAGS_INVISIBLE))
{
istat.is_user[1]--;
istat.is_user[0]++;
}
if (IsInvisible(sptr) && !(setflags & FLAGS_INVISIBLE))
{
istat.is_user[1]++;
istat.is_user[0]--;
}
if (IsMe(cptr)) /* bump ME out to let RMODE work -erra */
cptr = sptr;
send_umode_out(cptr, sptr, setflags);
}
/* update counters */
if (IsOper(sptr) && !(setflags & FLAGS_OPER))
{
istat.is_oper++;
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
sptr, ":%s MODE %s :+o", parv[0],
parv[0]);
#endif
}
else if (!IsOper(sptr) && (setflags & FLAGS_OPER))
{
istat.is_oper--;
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
sptr, ":%s MODE %s :-o", parv[0],
parv[0]);
#endif
}
else if (MyConnect(sptr) && !IsLocOp(sptr) && (setflags & FLAGS_LOCOP))
{
istat.is_oper--;
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
sptr, ":%s MODE %s :-O", parv[0],
parv[0]);
#endif
}
return penalty;
}
/*
* send the MODE string for user (user) to connection cptr
* -avalon
*/
void send_umode(cptr, sptr, old, sendmask, umode_buf)
aClient *cptr, *sptr;
int old, sendmask;
char *umode_buf;
{
Reg int *s, flag;
Reg char *m;
int what = MODE_NULL;
if (!sptr->user)
return;
/*
* build a string in umode_buf to represent the change in the user's
* mode between the new (sptr->flag) and 'old'.
*/
m = umode_buf;
*m = '\0';
for (s = user_modes; (flag = *s); s += 2)
{
if (MyClient(sptr) && !(flag & sendmask))
continue;
if ((flag & old) && !(sptr->user->flags & flag))
{
if (what == MODE_DEL)
*m++ = *(s+1);
else
{
what = MODE_DEL;
*m++ = '-';
*m++ = *(s+1);
}
}
else if (!(flag & old) && (sptr->user->flags & flag))
{
if (what == MODE_ADD)
*m++ = *(s+1);
else
{
what = MODE_ADD;
*m++ = '+';
*m++ = *(s+1);
}
}
}
*m = '\0';
if (*umode_buf && cptr)
sendto_one(cptr, ":%s MODE %s :%s",
sptr->name, sptr->name, umode_buf);
}
/*
* added Sat Jul 25 07:30:42 EST 1992
*/
void send_umode_out(cptr, sptr, old)
aClient *cptr, *sptr;
int old;
{
Reg int i;
Reg aClient *acptr;
send_umode(NULL, sptr, old, SEND_UMODES, buf);
if (*buf)
for (i = fdas.highest; i >= 0; i--)
{
if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr))
continue;
if (acptr == cptr || acptr == sptr)
continue;
sendto_one(acptr, ":%s MODE %s :%s",
sptr->name, sptr->name, buf);
}
if (cptr && MyClient(cptr))
send_umode(cptr, sptr, old, ALL_UMODES, buf);
#ifdef USE_SERVICES
/* buf contains all modes for local users, and iow only for remotes */
if (*buf)
check_services_butone(SERVICE_WANT_UMODE, NULL, sptr,
":%s MODE %s :%s", sptr->name,
sptr->name, buf);
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1