mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-28 21:46:38 +02:00
5124e60b7c
Fix force-rejoin not working if doing SVSMODE -x/+x (Koragg, #5015). Note to module coders: Please use the following procedure in case of an user/host change: * userhost_save_current(acptr); * << change username or hostname here (or both) >> * userhost_changed(acptr); This function will take care of notifying other clients about the userhost change, such as doing PART+JOIN+MODE if force-rejoin is enabled, and sending :xx CHGHOST user host messages to "CAP chghost" capable clients. Also, small note to everyone: If force-rejoin is enabled we will not send the PART+JOIN+MODE to "CAP chghost" capable clients. Doing so is just a hack to notify people of a userhost change. "CAP chghost" users can thus benefit from the reduced noise in this respect.
671 lines
15 KiB
C
671 lines
15 KiB
C
/*
|
|
* Unreal Internet Relay Chat Daemon, src/s_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 "macros.h"
|
|
#include "config.h"
|
|
#include "struct.h"
|
|
#include "common.h"
|
|
#include "sys.h"
|
|
#include "numeric.h"
|
|
#include "msg.h"
|
|
#include "channel.h"
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include "h.h"
|
|
#include "proto.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "version.h"
|
|
#endif
|
|
|
|
void send_umode_out(aClient *, aClient *, long);
|
|
void send_umode_out_nickv2(aClient *, aClient *, long);
|
|
void send_umode(aClient *, aClient *, long, long, char *);
|
|
void set_snomask(aClient *, char *);
|
|
void create_snomask(aClient *, anUser *, 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];
|
|
|
|
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, ":%s SETHOST :%s", sptr->name, sptr->user->virthost);
|
|
sptr->umodes |= UMODE_SETHOST;
|
|
|
|
userhost_changed(sptr);
|
|
|
|
sendto_one(sptr, err_str(RPL_HOSTHIDDEN), me.name, sptr->name, 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.
|
|
*/
|
|
|
|
/*
|
|
** 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(aClient *next, char *ch)
|
|
{
|
|
aClient *tmp = next;
|
|
|
|
next = find_client(ch, tmp);
|
|
if (tmp && list_empty(&next->client_node))
|
|
return NULL;
|
|
if (next != tmp)
|
|
return next;
|
|
|
|
tmp = next;
|
|
list_for_each_entry(next, &next->client_node, client_node)
|
|
{
|
|
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)
|
|
**
|
|
** 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, 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)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, sptr->name, 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)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, sptr->name, 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, 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;
|
|
}
|
|
|
|
/*
|
|
** check_for_target_limit
|
|
**
|
|
** Return Values:
|
|
** True(1) == too many targets are addressed
|
|
** False(0) == ok to send message
|
|
**
|
|
*/
|
|
int check_for_target_limit(aClient *sptr, void *target, const char *name)
|
|
{
|
|
#ifndef _WIN32 /* This is not windows compatible */
|
|
u_char *p;
|
|
#ifndef __alpha
|
|
u_int tmp = ((u_int)(intptr_t)target & 0xffff00) >> 8;
|
|
#else
|
|
u_int tmp = ((u_long)target & 0xffff00) >> 8;
|
|
#endif
|
|
u_char hash = (tmp * tmp) >> 12;
|
|
|
|
if (ValidatePermissionsForPath("immune:limits",sptr,NULL,NULL,NULL))
|
|
return 0;
|
|
if (sptr->local->targets[0] == hash)
|
|
return 0;
|
|
|
|
for (p = sptr->local->targets; p < &sptr->local->targets[MAXTARGETS - 1];)
|
|
if (*++p == hash)
|
|
{
|
|
/* move targethash to first position... */
|
|
memmove(&sptr->local->targets[1], &sptr->local->targets[0],
|
|
p - sptr->local->targets);
|
|
sptr->local->targets[0] = hash;
|
|
return 0;
|
|
}
|
|
|
|
if (TStime() < sptr->local->nexttarget)
|
|
{
|
|
sptr->local->since += TARGET_DELAY; /* lag them up */
|
|
sptr->local->nexttarget += TARGET_DELAY;
|
|
sendto_one(sptr, err_str(ERR_TARGETTOOFAST), me.name, sptr->name,
|
|
name, sptr->local->nexttarget - TStime());
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (TStime() > sptr->local->nexttarget + TARGET_DELAY*MAXTARGETS)
|
|
{
|
|
sptr->local->nexttarget = TStime() - TARGET_DELAY*MAXTARGETS;
|
|
}
|
|
|
|
sptr->local->nexttarget += TARGET_DELAY;
|
|
|
|
memmove(&sptr->local->targets[1], &sptr->local->targets[0], MAXTARGETS - 1);
|
|
sptr->local->targets[0] = hash;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** canonize
|
|
**
|
|
** reduce a string of duplicate list entries to contain only the unique
|
|
** items. Unavoidably O(n^2).
|
|
*/
|
|
extern 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void create_snomask(aClient *sptr, anUser *user, char *snomask) {
|
|
int what = MODE_ADD; /* keep this an int. -- Syzop */
|
|
char *p;
|
|
int i;
|
|
if (snomask == NULL) {
|
|
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)
|
|
user->snomask |= Snomask_Table[i].mode;
|
|
else
|
|
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, ":%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, ":%s UMODE2 %s",
|
|
sptr->name,
|
|
buf);
|
|
}
|
|
}
|
|
|
|
if (cptr && MyClient(cptr))
|
|
send_umode(cptr, sptr, old, ALL_UMODES, buf);
|
|
}
|
|
|
|
void send_umode_out_nickv2(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 (!SupportNICKv2(acptr) && (acptr != cptr)
|
|
&& (acptr != sptr) && *buf)
|
|
sendto_one(acptr, ":%s MODE %s :%s", sptr->name,
|
|
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)
|
|
sendto_one(sptr, err_str(ERR_SILELISTFULL), me.name, sptr->name, mask);
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (!match(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 = (char *)MyMalloc(strlen(mask) + 1);
|
|
(void)strcpy(lp->value.cp, mask);
|
|
sptr->user->silence = lp;
|
|
return 0;
|
|
}
|
|
|