1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-06-28 18:56:38 +02:00
Files
unrealircd/src/user.c
T
2019-08-23 20:28:48 +02:00

741 lines
17 KiB
C

/*
* Unreal Internet Relay Chat Daemon, src/user.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.
*/
/* s_user.c 2.74 2/8/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */
#include "unrealircd.h"
void send_umode_out(aClient *, aClient *, long);
void send_umode(aClient *, aClient *, long, long, char *);
void set_snomask(aClient *, char *);
extern int short_motd(aClient *sptr);
extern aChannel *get_channel(aClient *cptr, char *chname, int flag);
/* static Link *is_banned(aClient *, aChannel *); */
int dontspread = 0;
extern char *me_hash;
extern char backupbuf[];
static char buf[BUFSIZE];
int labeled_response_inhibit = 0;
void iNAH_host(aClient *sptr, char *host)
{
if (!sptr->user)
return;
userhost_save_current(sptr);
if (sptr->user->virthost)
{
MyFree(sptr->user->virthost);
sptr->user->virthost = NULL;
}
sptr->user->virthost = strdup(host);
if (MyConnect(sptr))
sendto_server(&me, 0, 0, NULL, ":%s SETHOST :%s", sptr->name, sptr->user->virthost);
sptr->umodes |= UMODE_SETHOST;
userhost_changed(sptr);
sendnumeric(sptr, RPL_HOSTHIDDEN, sptr->user->virthost);
}
long set_usermode(char *umode)
{
int newumode;
int what;
char *m;
int i;
newumode = 0;
what = MODE_ADD;
for (m = umode; *m; m++)
switch (*m)
{
case '+':
what = MODE_ADD;
break;
case '-':
what = MODE_DEL;
break;
case ' ':
case '\n':
case '\r':
case '\t':
break;
default:
for (i = 0; i <= Usermode_highest; i++)
{
if (!Usermode_Table[i].flag)
continue;
if (*m == Usermode_Table[i].flag)
{
if (what == MODE_ADD)
newumode |= Usermode_Table[i].mode;
else
newumode &= ~Usermode_Table[i].mode;
}
}
}
return (newumode);
}
/*
** 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[1]...parv[parc-1]
** pointers to additional parameters
** parv[parc] == NULL, *always*
**
** note: it is guaranteed that parv[1]..parv[parc-1] are all
** non-NULL pointers.
*/
/*
** 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)
**
** Rewritten by Syzop / Oct 2015. This function was rather
** complex and no longer understandable. It also was responsible
** for mysterious issues and crashes. Hence rewritten.
*/
int hunt_server(aClient *cptr, aClient *sptr, MessageTag *mtags, char *command, int server, int parc, char *parv[])
{
aClient *acptr;
char *saved;
/* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */
if (parc <= server || BadPtr(parv[server]))
return HUNTED_ISME;
acptr = find_client(parv[server], NULL);
/* find_client() may find a variety of clients. Only servers/persons please, no 'unknowns'. */
if (acptr && MyConnect(acptr) && !IsMe(acptr) && !IsPerson(acptr) && !IsServer(acptr))
acptr = NULL;
if (!acptr)
{
sendnumeric(sptr, ERR_NOSUCHSERVER, parv[server]);
return HUNTED_NOSUCH;
}
if (IsMe(acptr) || MyClient(acptr))
return HUNTED_ISME;
/* Never send the message back from where it came from */
if (acptr->from == sptr->from)
{
sendnumeric(sptr, ERR_NOSUCHSERVER, parv[server]);
return HUNTED_NOSUCH;
}
/* Replace "server" part with actual servername (eg: 'User' -> 'x.y.net')
* Ugly. Previous version didn't even restore the state, now we do.
*/
saved = parv[server];
parv[server] = acptr->name;
sendto_one(acptr, mtags, command, sptr->name,
parv[1], parv[2], parv[3], parv[4],
parv[5], parv[6], parv[7], parv[8]);
parv[server] = saved;
return HUNTED_PASS;
}
/** Convert a target pointer to an 8 bit hash, used for target limiting. */
unsigned char hash_target(void *target)
{
unsigned long long v = (unsigned long long)target;
/* ircu does >> 16 and 8 but since our sizeof(aClient) is
* towards 512 (and hence the alignment), that bit is useless.
* So we do >> 17 and 9.
*/
return (unsigned char)((v >> 17) ^ (v >> 9));
}
/** check_for_target_limit
* @param sptr The client.
* @param target The target client
* @param name The name of the target client (used in the error message)
* @retval Returns 1 if too many targets were addressed (do not send!), 0 if ok to send.
*/
int check_for_target_limit(aClient *sptr, void *target, const char *name)
{
u_char *p;
u_char hash = hash_target(target);
int i;
if (ValidatePermissionsForPath("immune:target-limit",sptr,NULL,NULL,NULL))
return 0;
if (sptr->local->targets[0] == hash)
return 0;
for (i = 1; i < iConf.max_concurrent_conversations_users; i++)
{
if (sptr->local->targets[i] == hash)
{
/* Move this target hash to the first position */
memmove(&sptr->local->targets[1], &sptr->local->targets[0], i);
sptr->local->targets[0] = hash;
return 0;
}
}
if (TStime() < sptr->local->nexttarget)
{
/* Target limit reached */
sptr->local->nexttarget += 2; /* punish them some more */
sptr->local->since += 2; /* lag them up as well */
sendnumeric(sptr, ERR_TARGETTOOFAST,
name, sptr->local->nexttarget - TStime());
return 1;
}
/* If not set yet or in the very past, then adjust it.
* This is so sptr->local->nexttarget=0 will become sptr->local->nexttarget=currenttime-...
*/
if (TStime() > sptr->local->nexttarget +
(iConf.max_concurrent_conversations_users * iConf.max_concurrent_conversations_new_user_every))
{
sptr->local->nexttarget = TStime() - ((iConf.max_concurrent_conversations_users-1) * iConf.max_concurrent_conversations_new_user_every);
}
sptr->local->nexttarget += iConf.max_concurrent_conversations_new_user_every;
/* Add the new target (first move the rest, then add us at position 0 */
memmove(&sptr->local->targets[1], &sptr->local->targets[0], iConf.max_concurrent_conversations_users - 1);
sptr->local->targets[0] = hash;
return 0;
}
/*
** canonize
**
** reduce a string of duplicate list entries to contain only the unique
** items. Unavoidably O(n^2).
*/
char *canonize(char *buffer)
{
static char cbuf[2048];
char *s, *t, *cp = cbuf;
int l = 0;
char *p = NULL, *p2;
*cp = '\0';
if (!buffer)
return NULL;
/* Ohh.. so lazy. But then again, this should never happen with a 2K buffer anyway. */
if (strlen(buffer) >= sizeof(cbuf))
buffer[sizeof(cbuf)-1] = '\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 (!mycmp(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;
}
/*
** get_mode_str
** by vmlinuz
** returns an ascii string of modes
*/
char *get_sno_str(aClient *sptr) {
int i;
char *m;
m = buf;
*m++ = '+';
for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++)
if (Snomask_Table[i].flag && sptr->user->snomask & Snomask_Table[i].mode)
*m++ = Snomask_Table[i].flag;
*m = 0;
return buf;
}
char *get_mode_str(aClient *acptr)
{
int i;
char *m;
m = buf;
*m++ = '+';
for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++)
if (Usermode_Table[i].flag && (acptr->umodes & Usermode_Table[i].mode))
*m++ = Usermode_Table[i].flag;
*m = '\0';
return buf;
}
char *get_modestr(long umodes)
{
int i;
char *m;
m = buf;
*m++ = '+';
for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++)
if (Usermode_Table[i].flag && (umodes & Usermode_Table[i].mode))
*m++ = Usermode_Table[i].flag;
*m = '\0';
return buf;
}
char *get_snostr(long sno) {
int i;
char *m;
m = buf;
*m++ = '+';
for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++)
if (Snomask_Table[i].flag && sno & Snomask_Table[i].mode)
*m++ = Snomask_Table[i].flag;
*m = 0;
return buf;
}
void set_snomask(aClient *sptr, char *snomask) {
int what = MODE_ADD; /* keep this an int. -- Syzop */
char *p;
int i;
if (snomask == NULL) {
sptr->user->snomask = 0;
return;
}
for (p = snomask; p && *p; p++) {
switch (*p) {
case '+':
what = MODE_ADD;
break;
case '-':
what = MODE_DEL;
break;
default:
for (i = 0; i <= Snomask_highest; i++)
{
if (!Snomask_Table[i].flag)
continue;
if (*p == Snomask_Table[i].flag)
{
if (Snomask_Table[i].allowed && !Snomask_Table[i].allowed(sptr,what))
continue;
if (what == MODE_ADD)
sptr->user->snomask |= Snomask_Table[i].mode;
else
sptr->user->snomask &= ~Snomask_Table[i].mode;
}
}
}
}
}
/*
* send the MODE string for user (user) to connection cptr
* -avalon
*/
void send_umode(aClient *cptr, aClient *sptr, long old, long sendmask, char *umode_buf)
{
int i;
long flag;
char *m;
int what = MODE_NULL;
/*
* 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 (i = 0; i <= Usermode_highest; i++)
{
if (!Usermode_Table[i].flag)
continue;
flag = Usermode_Table[i].mode;
if (MyClient(sptr) && !(flag & sendmask))
continue;
if ((flag & old) && !(sptr->umodes & flag))
{
if (what == MODE_DEL)
*m++ = Usermode_Table[i].flag;
else
{
what = MODE_DEL;
*m++ = '-';
*m++ = Usermode_Table[i].flag;
}
}
else if (!(flag & old) && (sptr->umodes & flag))
{
if (what == MODE_ADD)
*m++ = Usermode_Table[i].flag;
else
{
what = MODE_ADD;
*m++ = '+';
*m++ = Usermode_Table[i].flag;
}
}
}
*m = '\0';
if (*umode_buf && cptr)
sendto_one(cptr, NULL, ":%s MODE %s :%s", sptr->name,
sptr->name, umode_buf);
}
/*
* added Sat Jul 25 07:30:42 EST 1992
*/
void send_umode_out(aClient *cptr, aClient *sptr, long old)
{
aClient *acptr;
send_umode(NULL, sptr, old, SEND_UMODES, buf);
list_for_each_entry(acptr, &server_list, special_node)
{
if ((acptr != cptr) && (acptr != sptr) && *buf)
{
sendto_one(acptr, NULL, ":%s UMODE2 %s",
sptr->name,
buf);
}
}
if (cptr && MyClient(cptr))
send_umode(cptr, sptr, old, ALL_UMODES, buf);
}
int del_silence(aClient *sptr, char *mask)
{
Link **lp;
Link *tmp;
for (lp = &(sptr->user->silence); *lp; lp = &((*lp)->next))
if (mycmp(mask, (*lp)->value.cp) == 0)
{
tmp = *lp;
*lp = tmp->next;
MyFree(tmp->value.cp);
free_link(tmp);
return 0;
}
return -1;
}
int add_silence(aClient *sptr, char *mask, int senderr)
{
Link *lp;
int cnt = 0;
for (lp = sptr->user->silence; lp; lp = lp->next)
{
if (MyClient(sptr))
if ((strlen(lp->value.cp) > MAXSILELENGTH) || (++cnt >= SILENCE_LIMIT))
{
if (senderr)
sendnumeric(sptr, ERR_SILELISTFULL, mask);
return -1;
}
else
{
if (match_simple(lp->value.cp, mask))
return -1;
}
else if (!mycmp(lp->value.cp, mask))
return -1;
}
lp = make_link();
bzero((char *)lp, sizeof(Link));
lp->next = sptr->user->silence;
lp->value.cp = strdup(mask);
sptr->user->silence = lp;
return 0;
}
static MaxTarget *maxtargets = NULL; /**< For set::max-targets-per-command configuration */
static void maxtarget_add_sorted(MaxTarget *n)
{
MaxTarget *e;
if (!maxtargets)
{
maxtargets = n;
return;
}
for (e = maxtargets; e; e = e->next)
{
if (strcmp(n->cmd, e->cmd) < 0)
{
/* Insert us before */
if (e->prev)
e->prev->next = n;
else
maxtargets = n; /* new head */
n->prev = e->prev;
n->next = e;
e->prev = n;
return;
}
if (!e->next)
{
/* Append us at end */
e->next = n;
n->prev = e;
return;
}
}
}
/** Find a maxtarget structure for a cmd (internal) */
MaxTarget *findmaxtarget(char *cmd)
{
MaxTarget *m;
for (m = maxtargets; m; m = m->next)
if (!strcasecmp(m->cmd, cmd))
return m;
return NULL;
}
/** Set a maximum targets per command restriction */
void setmaxtargets(char *cmd, int limit)
{
MaxTarget *m = findmaxtarget(cmd);
if (!m)
{
char cmdupper[64], *i, *o;
if (strlen(cmd) > 63)
cmd[63] = '\0';
for (i=cmd,o=cmdupper; *i; i++)
*o++ = toupper(*i);
*o = '\0';
m = MyMallocEx(sizeof(MaxTarget));
m->cmd = strdup(cmdupper);
maxtarget_add_sorted(m);
}
m->limit = limit;
}
/** Free all set::max-targets-per-command configuration (internal) */
void freemaxtargets(void)
{
MaxTarget *m, *m_next;
for (m = maxtargets; m; m = m_next)
{
m_next = m->next;
safefree(m->cmd);
MyFree(m);
}
maxtargets = NULL;
}
/** Return the maximum number of targets permitted for a command */
int max_targets_for_command(char *cmd)
{
MaxTarget *m = findmaxtarget(cmd);
if (m)
return m->limit;
return 1; /* default to 1 */
}
void set_isupport_targmax(void)
{
char buf[512], tbuf[64];
MaxTarget *m;
*buf = '\0';
for (m = maxtargets; m; m = m->next)
{
if (m->limit == MAXTARGETS_MAX)
snprintf(tbuf, sizeof(tbuf), "%s:", m->cmd);
else
snprintf(tbuf, sizeof(tbuf), "%s:%d", m->cmd, m->limit);
if (*buf)
strlcat(buf, ",", sizeof(buf));
strlcat(buf, tbuf, sizeof(buf));
}
IsupportSet(NULL, "TARGMAX", buf);
}
/** Called between config test and config run */
void set_targmax_defaults(void)
{
/* Free existing... */
freemaxtargets();
/* Set the defaults */
setmaxtargets("PRIVMSG", 4);
setmaxtargets("NOTICE", 1);
setmaxtargets("NAMES", 1); // >1 is not supported
setmaxtargets("WHOIS", 1);
setmaxtargets("WHOWAS", 1); // >1 is not supported
setmaxtargets("KICK", 4);
setmaxtargets("LIST", MAXTARGETS_MAX);
setmaxtargets("JOIN", MAXTARGETS_MAX);
setmaxtargets("PART", MAXTARGETS_MAX);
setmaxtargets("SAJOIN", MAXTARGETS_MAX);
setmaxtargets("SAPART", MAXTARGETS_MAX);
setmaxtargets("KILL", MAXTARGETS_MAX);
setmaxtargets("DCCALLOW", MAXTARGETS_MAX);
/* The following 3 are space-separated (and actually the previous
* mentioned DCCALLOW is both space-and-comma separated).
* It seems most IRCd's don't list space-separated targets list
* in TARGMAX... On the other hand, why not? It says nowhere in
* the TARGMAX specification that it's only for comma-separated
* commands. So let's be nice and consistent and inform the
* clients about the limits for such commands as well:
*/
setmaxtargets("USERHOST", MAXTARGETS_MAX); // not configurable
setmaxtargets("USERIP", MAXTARGETS_MAX); // not configurable
setmaxtargets("ISON", MAXTARGETS_MAX); // not configurable
setmaxtargets("WATCH", MAXTARGETS_MAX); // not configurable
}
/** Is the user handshake finished and can register_user() be called?
* This checks things like: do we have a NICK, USER, nospoof,
* and any other things modules may add:
* eg: the cap module checks if client capability negotiation
* is in progress
*/
int is_handshake_finished(aClient *sptr)
{
Hook *h;
int n;
for (h = Hooks[HOOKTYPE_IS_HANDSHAKE_FINISHED]; h; h = h->next)
{
n = (*(h->func.intfunc))(sptr);
if (n == 0)
return 0; /* We can stop already */
}
/* I figured these can be here, in the core: */
if (sptr->user && *sptr->user->username && sptr->name[0] && IsNotSpoof(sptr))
return 1;
return 0;
}