mirror of https://github.com/pissnet/pissircd.git
908 lines
25 KiB
C
908 lines
25 KiB
C
/*
|
|
* IRC - Internet Relay Chat, src/modules/sjoin.c
|
|
* (C) 2004 The UnrealIRCd Team
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
CMD_FUNC(cmd_sjoin);
|
|
|
|
#define MSG_SJOIN "SJOIN"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"sjoin",
|
|
"5.1",
|
|
"command /sjoin",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-5",
|
|
};
|
|
|
|
MOD_INIT()
|
|
{
|
|
CommandAdd(modinfo->handle, MSG_SJOIN, cmd_sjoin, MAXPARA, CMD_SERVER);
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
typedef struct xParv aParv;
|
|
struct xParv {
|
|
int parc;
|
|
char *parv[256];
|
|
};
|
|
|
|
aParv pparv;
|
|
|
|
aParv *mp2parv(char *xmbuf, char *parmbuf)
|
|
{
|
|
int c;
|
|
char *p, *s;
|
|
|
|
pparv.parv[0] = xmbuf;
|
|
c = 1;
|
|
|
|
for (s = strtoken(&p, parmbuf, " "); s; s = strtoken(&p, NULL, " "))
|
|
{
|
|
pparv.parv[c] = s;
|
|
c++; /* in my dreams */
|
|
}
|
|
pparv.parv[c] = NULL;
|
|
pparv.parc = c;
|
|
return (&pparv);
|
|
}
|
|
|
|
void send_local_chan_mode(MessageTag *recv_mtags, Client *client, Channel *channel, char *modebuf, char *parabuf)
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
|
|
new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf);
|
|
sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags,
|
|
":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf);
|
|
if (MyConnect(client))
|
|
RunHook7(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1);
|
|
else
|
|
RunHook7(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1);
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
/** SJOIN: Synchronize channel modes, +beI lists and users (server-to-server command)
|
|
* Extensive technical documentation is available at:
|
|
* https://www.unrealircd.org/docs/Server_protocol:SJOIN_command
|
|
*
|
|
* parv[1] = channel timestamp
|
|
* parv[2] = channel name
|
|
*
|
|
* if parc == 3:
|
|
* parv[3] = nick names + modes - all in one parameter
|
|
*
|
|
* if parc == 4:
|
|
* parv[3] = channel modes
|
|
* parv[4] = nick names + modes - all in one parameter
|
|
*
|
|
* if parc > 4:
|
|
* parv[3] = channel modes
|
|
* parv[4 to parc - 2] = mode parameters
|
|
* parv[parc - 1] = nick names + modes
|
|
*/
|
|
|
|
/* Note: with regards to message tags we use new_message_special()
|
|
* here extensively. This because one SJOIN command can (often)
|
|
* generate multiple events that are sent to clients,
|
|
* for example 1 SJOIN can cause multiple joins, +beI, etc.
|
|
* -- Syzop
|
|
*/
|
|
|
|
/* Some ugly macros, but useful */
|
|
#define Addit(mode,param) if ((strlen(parabuf) + strlen(param) + 11 < MODEBUFLEN) && (b <= MAXMODEPARAMS)) { \
|
|
if (*parabuf) \
|
|
strcat(parabuf, " ");\
|
|
strcat(parabuf, param);\
|
|
modebuf[b++] = mode;\
|
|
modebuf[b] = 0;\
|
|
}\
|
|
else {\
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); \
|
|
strcpy(parabuf,param);\
|
|
/* modebuf[0] should stay what it was ('+' or '-') */ \
|
|
modebuf[1] = mode;\
|
|
modebuf[2] = '\0';\
|
|
b = 2;\
|
|
}
|
|
#define Addsingle(x) do { modebuf[b] = x; b++; modebuf[b] = '\0'; } while(0)
|
|
#define CheckStatus(x,y) do { if (modeflags & (y)) { Addit((x), acptr->name); } } while(0)
|
|
|
|
CMD_FUNC(cmd_sjoin)
|
|
{
|
|
unsigned short nopara;
|
|
unsigned short nomode; /**< An SJOIN without MODE? */
|
|
unsigned short removeours; /**< Remove our modes */
|
|
unsigned short removetheirs; /**< Remove their modes (or actually: do not ADD their modes, the MODE -... line will be sent later by the other side) */
|
|
unsigned short merge; /**< same timestamp: merge their & our modes */
|
|
char pvar[MAXMODEPARAMS][MODEBUFLEN + 3];
|
|
char cbuf[1024];
|
|
char nick[1024]; /**< nick or ban/invex/exempt being processed */
|
|
char scratch_buf[1024]; /**< scratch buffer */
|
|
char prefix[16]; /**< prefix of nick for server to server traffic (eg: @) */
|
|
char uid_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID) */
|
|
char uid_sjsby_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID and SJSBY) */
|
|
char sj3_parabuf[BUFSIZE]; /**< Prefix for the above SJOIN buffers (":xxx SJOIN #channel +mode :") */
|
|
char *s = NULL;
|
|
Channel *channel; /**< Channel */
|
|
aParv *ap;
|
|
int pcount, i;
|
|
Hook *h;
|
|
time_t ts, oldts;
|
|
unsigned short b=0;
|
|
char *tp, *p, *saved = NULL;
|
|
long modeflags;
|
|
char queue_s=0, queue_c=0; /* oh this is soooooo ugly :p */
|
|
|
|
if (!IsServer(client) || parc < 4)
|
|
return;
|
|
|
|
if (!IsChannelName(parv[2]))
|
|
return;
|
|
|
|
merge = nopara = nomode = removeours = removetheirs = 0;
|
|
|
|
if (parc < 6)
|
|
nopara = 1;
|
|
|
|
if (parc < 5)
|
|
nomode = 1;
|
|
|
|
channel = get_channel(client, parv[2], CREATE);
|
|
|
|
ts = (time_t)atol(parv[1]);
|
|
|
|
if (channel->creationtime > ts)
|
|
{
|
|
removeours = 1;
|
|
oldts = channel->creationtime;
|
|
channel->creationtime = ts;
|
|
}
|
|
else if ((channel->creationtime < ts) && (channel->creationtime != 0))
|
|
{
|
|
removetheirs = 1;
|
|
}
|
|
else if (channel->creationtime == ts)
|
|
{
|
|
merge = 1;
|
|
}
|
|
|
|
if (channel->creationtime == 0)
|
|
{
|
|
oldts = -1;
|
|
channel->creationtime = ts;
|
|
}
|
|
else
|
|
{
|
|
oldts = channel->creationtime;
|
|
}
|
|
|
|
// FIXME: make it so services cannot screw this up so easily --- if possible...
|
|
if (ts < 750000)
|
|
{
|
|
if (ts != 0)
|
|
sendto_ops
|
|
("Warning! Possible desync: SJOIN for channel %s has a fishy timestamp (%lld) [%s/%s]",
|
|
channel->chname, (long long)ts, client->name, client->direction->name);
|
|
}
|
|
|
|
parabuf[0] = '\0';
|
|
modebuf[0] = '+';
|
|
modebuf[1] = '\0';
|
|
|
|
/* Grab current modes -> modebuf & parabuf */
|
|
channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 1);
|
|
|
|
/* Do we need to remove all our modes, bans/exempt/inves lists and -vhoaq our users? */
|
|
if (removeours)
|
|
{
|
|
Member *lp;
|
|
Membership *lp2;
|
|
|
|
modebuf[0] = '-';
|
|
|
|
/* remove our modes if any */
|
|
if (modebuf[1] != '\0')
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
ap = mp2parv(modebuf, parabuf);
|
|
set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0);
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf);
|
|
}
|
|
/* remove bans */
|
|
/* reset the buffers */
|
|
modebuf[0] = '-';
|
|
modebuf[1] = '\0';
|
|
parabuf[0] = '\0';
|
|
b = 1;
|
|
while(channel->banlist)
|
|
{
|
|
Ban *ban = channel->banlist;
|
|
Addit('b', ban->banstr);
|
|
channel->banlist = ban->next;
|
|
safe_free(ban->banstr);
|
|
safe_free(ban->who);
|
|
free_ban(ban);
|
|
}
|
|
while(channel->exlist)
|
|
{
|
|
Ban *ban = channel->exlist;
|
|
Addit('e', ban->banstr);
|
|
channel->exlist = ban->next;
|
|
safe_free(ban->banstr);
|
|
safe_free(ban->who);
|
|
free_ban(ban);
|
|
}
|
|
while(channel->invexlist)
|
|
{
|
|
Ban *ban = channel->invexlist;
|
|
Addit('I', ban->banstr);
|
|
channel->invexlist = ban->next;
|
|
safe_free(ban->banstr);
|
|
safe_free(ban->who);
|
|
free_ban(ban);
|
|
}
|
|
for (lp = channel->members; lp; lp = lp->next)
|
|
{
|
|
lp2 = find_membership_link(lp->client->user->channel, channel);
|
|
if (!lp2)
|
|
{
|
|
sendto_realops("Oops! channel->members && !find_membership_link");
|
|
continue;
|
|
}
|
|
if (lp->flags & MODE_CHANOWNER)
|
|
{
|
|
lp->flags &= ~MODE_CHANOWNER;
|
|
Addit('q', lp->client->name);
|
|
}
|
|
if (lp->flags & MODE_CHANADMIN)
|
|
{
|
|
lp->flags &= ~MODE_CHANADMIN;
|
|
Addit('a', lp->client->name);
|
|
}
|
|
if (lp->flags & MODE_CHANOP)
|
|
{
|
|
lp->flags &= ~MODE_CHANOP;
|
|
Addit('o', lp->client->name);
|
|
}
|
|
if (lp->flags & MODE_HALFOP)
|
|
{
|
|
lp->flags &= ~MODE_HALFOP;
|
|
Addit('h', lp->client->name);
|
|
}
|
|
if (lp->flags & MODE_VOICE)
|
|
{
|
|
lp->flags &= ~MODE_VOICE;
|
|
Addit('v', lp->client->name);
|
|
}
|
|
/* Those should always match anyways */
|
|
lp2->flags = lp->flags;
|
|
}
|
|
if (b > 1)
|
|
{
|
|
modebuf[b] = '\0';
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf);
|
|
}
|
|
|
|
/* since we're dropping our modes, we want to clear the mlock as well. --nenolod */
|
|
set_channel_mlock(client, channel, NULL, FALSE);
|
|
}
|
|
/* Mode setting done :), now for our beloved clients */
|
|
parabuf[0] = 0;
|
|
modebuf[0] = '+';
|
|
modebuf[1] = '\0';
|
|
b = 1;
|
|
strlcpy(cbuf, parv[parc-1], sizeof cbuf);
|
|
|
|
sj3_parabuf[0] = '\0';
|
|
for (i = 2; i <= (parc - 2); i++)
|
|
{
|
|
if (!parv[i])
|
|
{
|
|
sendto_ops("Got null parv in SJ3 code");
|
|
continue;
|
|
}
|
|
strlcat(sj3_parabuf, parv[i], sizeof sj3_parabuf);
|
|
if (((i + 1) <= (parc - 2)))
|
|
strlcat(sj3_parabuf, " ", sizeof sj3_parabuf);
|
|
}
|
|
|
|
/* Now process adding of users & adding of list modes (bans/exempt/invex) */
|
|
|
|
snprintf(uid_buf, sizeof uid_buf, ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
snprintf(uid_sjsby_buf, sizeof uid_sjsby_buf, ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
|
|
for (s = strtoken(&saved, cbuf, " "); s; s = strtoken(&saved, NULL, " "))
|
|
{
|
|
char *setby = client->name; /**< Set by (nick, nick!user@host, or server name) */
|
|
time_t setat = TStime(); /**< Set at timestamp */
|
|
int sjsby_info = 0; /**< Set to 1 if we receive SJSBY info to alter the above 2 vars */
|
|
|
|
modeflags = 0;
|
|
i = 0;
|
|
tp = s;
|
|
|
|
/* UnrealIRCd 4.2.2 and later support "SJSBY" which allows communicating
|
|
* setat/setby information for bans, ban exempts and invite exceptions.
|
|
*/
|
|
if (SupportSJSBY(client->direction) && (*tp == '<'))
|
|
{
|
|
/* Special prefix to communicate timestamp and setter:
|
|
* "<" + timestamp + "," + nick[!user@host] + ">" + normal SJOIN stuff
|
|
* For example: "<12345,nick>&some!nice@ban"
|
|
*/
|
|
char *end = strchr(tp, '>'), *p;
|
|
if (!end)
|
|
{
|
|
/* this obviously should never happen */
|
|
sendto_ops("Malformed SJOIN piece from %s for channel %s: %s",
|
|
client->name, channel->chname, tp);
|
|
continue;
|
|
}
|
|
*end++ = '\0';
|
|
|
|
p = strchr(tp, ',');
|
|
if (!p)
|
|
{
|
|
/* missing setby parameter */
|
|
sendto_ops("Malformed SJOIN piece from %s for channel %s: %s",
|
|
client->name, channel->chname, tp);
|
|
continue;
|
|
}
|
|
*p++ = '\0';
|
|
|
|
setat = atol(tp+1);
|
|
setby = p;
|
|
sjsby_info = 1;
|
|
|
|
tp = end; /* the remainder is used for the actual ban/exempt/invex */
|
|
}
|
|
|
|
while (
|
|
(*tp == '@') || (*tp == '+') || (*tp == '%')
|
|
|| (*tp == '*') || (*tp == '~') || (*tp == '&')
|
|
|| (*tp == '"') || (*tp == '\''))
|
|
{
|
|
switch (*(tp++))
|
|
{
|
|
case '@':
|
|
modeflags |= CHFL_CHANOP;
|
|
break;
|
|
case '%':
|
|
modeflags |= CHFL_HALFOP;
|
|
break;
|
|
case '+':
|
|
modeflags |= CHFL_VOICE;
|
|
break;
|
|
case '*':
|
|
modeflags |= CHFL_CHANOWNER;
|
|
break;
|
|
case '~':
|
|
modeflags |= CHFL_CHANADMIN;
|
|
break;
|
|
case '&':
|
|
modeflags = CHFL_BAN;
|
|
goto getnick;
|
|
case '"':
|
|
modeflags = CHFL_EXCEPT;
|
|
goto getnick;
|
|
case '\'':
|
|
modeflags = CHFL_INVEX;
|
|
goto getnick;
|
|
}
|
|
}
|
|
getnick:
|
|
|
|
/* First, set the appropriate prefix for server to server traffic.
|
|
* Note that 'prefix' is a 16 byte buffer but it's safe due to the limited
|
|
* number of choices as can be seen below:
|
|
*/
|
|
*prefix = '\0';
|
|
p = prefix;
|
|
if (modeflags == CHFL_INVEX)
|
|
*p++ = '\'';
|
|
else if (modeflags == CHFL_EXCEPT)
|
|
*p++ = '\"';
|
|
else if (modeflags == CHFL_BAN)
|
|
*p++ = '&';
|
|
else
|
|
{
|
|
/* multiple options possible at the same time */
|
|
if (modeflags & CHFL_CHANOWNER)
|
|
*p++ = '*';
|
|
if (modeflags & CHFL_CHANADMIN)
|
|
*p++ = '~';
|
|
if (modeflags & CHFL_CHANOP)
|
|
*p++ = '@';
|
|
if (modeflags & CHFL_HALFOP)
|
|
*p++ = '%';
|
|
if (modeflags & CHFL_VOICE)
|
|
*p++ = '+';
|
|
}
|
|
*p = '\0';
|
|
|
|
/* Now copy the "nick" (which can actually be a ban/invex/exempt).
|
|
* There's no size checking here but nick is 1024 bytes and we
|
|
* have 512 bytes input max.
|
|
*/
|
|
i = 0;
|
|
while ((*tp != ' ') && (*tp != '\0'))
|
|
nick[i++] = *(tp++); /* get nick */
|
|
nick[i] = '\0';
|
|
if (nick[0] == ' ')
|
|
continue;
|
|
if (nick[0] == '\0')
|
|
continue;
|
|
Debug((DEBUG_DEBUG, "Got nick: %s", nick));
|
|
if (!(modeflags & CHFL_BAN) && !(modeflags & CHFL_EXCEPT) && !(modeflags & CHFL_INVEX))
|
|
{
|
|
Client *acptr;
|
|
|
|
/* A person joining */
|
|
|
|
/* The user may no longer exist. This can happen in case of a
|
|
* SVSKILL traveling in the other direction. Nothing to worry about.
|
|
*/
|
|
if (!(acptr = find_person(nick, NULL)))
|
|
continue;
|
|
|
|
if (acptr->direction != client->direction)
|
|
{
|
|
if (IsMember(acptr, channel))
|
|
{
|
|
/* Nick collision, don't kick or it desyncs -Griever*/
|
|
continue;
|
|
}
|
|
|
|
sendto_one(client, NULL,
|
|
":%s KICK %s %s :Fake direction",
|
|
me.id, channel->chname, acptr->name);
|
|
sendto_realops
|
|
("Fake direction from user %s in SJOIN from %s(%s) at %s",
|
|
nick, client->srvptr->name,
|
|
client->name, channel->chname);
|
|
continue;
|
|
}
|
|
|
|
if (removetheirs)
|
|
{
|
|
modeflags = 0;
|
|
}
|
|
|
|
if (!IsMember(acptr, channel))
|
|
{
|
|
/* User joining the channel, send JOIN to local users.
|
|
*/
|
|
MessageTag *mtags = NULL;
|
|
|
|
add_user_to_channel(channel, acptr, modeflags);
|
|
RunHook4(HOOKTYPE_REMOTE_JOIN, acptr, channel, recv_mtags, NULL);
|
|
new_message_special(acptr, recv_mtags, &mtags, ":%s JOIN %s", acptr->name, channel->chname);
|
|
send_join_to_local_users(acptr, channel, mtags);
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
CheckStatus('q', CHFL_CHANOWNER);
|
|
CheckStatus('a', CHFL_CHANADMIN);
|
|
CheckStatus('o', CHFL_CHANOP);
|
|
CheckStatus('h', CHFL_HALFOP);
|
|
CheckStatus('v', CHFL_VOICE);
|
|
|
|
if (strlen(uid_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10)
|
|
{
|
|
/* Send what we have and start a new buffer */
|
|
sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf);
|
|
snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
/* Double-check the new buffer is sufficient to concat the data */
|
|
if (strlen(uid_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5)
|
|
{
|
|
ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'",
|
|
uid_buf, prefix, acptr->id);
|
|
sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname);
|
|
continue;
|
|
}
|
|
}
|
|
sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, acptr->id);
|
|
|
|
if (strlen(uid_sjsby_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10)
|
|
{
|
|
/* Send what we have and start a new buffer */
|
|
sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf);
|
|
snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
/* Double-check the new buffer is sufficient to concat the data */
|
|
if (strlen(uid_sjsby_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5)
|
|
{
|
|
ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'",
|
|
uid_sjsby_buf, prefix, acptr->id);
|
|
sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname);
|
|
continue;
|
|
}
|
|
}
|
|
sprintf(uid_sjsby_buf+strlen(uid_sjsby_buf), "%s%s ", prefix, acptr->id);
|
|
}
|
|
else
|
|
{
|
|
if (removetheirs)
|
|
continue;
|
|
|
|
/* For list modes (beI): validate the syntax */
|
|
if (modeflags & (CHFL_BAN|CHFL_EXCEPT|CHFL_INVEX))
|
|
{
|
|
char *str;
|
|
|
|
/* non-extbans: prevent bans without ! or @. a good case of "should never happen". */
|
|
if ((nick[0] != '~') && (!strchr(nick, '!') || !strchr(nick, '@') || (nick[0] == '!')))
|
|
continue;
|
|
|
|
str = clean_ban_mask(nick, MODE_ADD, client);
|
|
if (!str)
|
|
continue; /* invalid ban syntax */
|
|
strlcpy(nick, str, sizeof(nick));
|
|
}
|
|
|
|
/* Adding of list modes */
|
|
if (modeflags & CHFL_BAN)
|
|
{
|
|
if (add_listmode_ex(&channel->banlist, client, channel, nick, setby, setat) != -1)
|
|
{
|
|
Addit('b', nick);
|
|
}
|
|
}
|
|
if (modeflags & CHFL_EXCEPT)
|
|
{
|
|
if (add_listmode_ex(&channel->exlist, client, channel, nick, setby, setat) != -1)
|
|
{
|
|
Addit('e', nick);
|
|
}
|
|
}
|
|
if (modeflags & CHFL_INVEX)
|
|
{
|
|
if (add_listmode_ex(&channel->invexlist, client, channel, nick, setby, setat) != -1)
|
|
{
|
|
Addit('I', nick);
|
|
}
|
|
}
|
|
|
|
if (strlen(uid_buf) + strlen(prefix) + strlen(nick) > BUFSIZE - 10)
|
|
{
|
|
/* Send what we have and start a new buffer */
|
|
sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf);
|
|
snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
/* Double-check the new buffer is sufficient to concat the data */
|
|
if (strlen(uid_buf) + strlen(prefix) + strlen(nick) > BUFSIZE - 5)
|
|
{
|
|
ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'",
|
|
uid_buf, prefix, nick);
|
|
sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname);
|
|
continue;
|
|
}
|
|
}
|
|
sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, nick);
|
|
|
|
*scratch_buf = '\0';
|
|
if (sjsby_info)
|
|
add_sjsby(scratch_buf, setby, setat);
|
|
strcat(scratch_buf, prefix);
|
|
strcat(scratch_buf, nick);
|
|
strcat(scratch_buf, " ");
|
|
if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 10)
|
|
{
|
|
/* Send what we have and start a new buffer */
|
|
sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf);
|
|
snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf);
|
|
/* Double-check the new buffer is sufficient to concat the data */
|
|
if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 5)
|
|
{
|
|
ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s'", uid_sjsby_buf, scratch_buf);
|
|
sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname);
|
|
continue;
|
|
}
|
|
}
|
|
strcpy(uid_sjsby_buf+strlen(uid_sjsby_buf), scratch_buf); /* size already checked above */
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Send out any possible remainder.. */
|
|
Debug((DEBUG_DEBUG, "Sending '%li %s :%s' to ", ts, parabuf, parv[parc - 1]));
|
|
sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf);
|
|
sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf);
|
|
|
|
if (modebuf[1])
|
|
{
|
|
modebuf[b] = '\0';
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf);
|
|
}
|
|
|
|
if (!merge && !removetheirs && !nomode)
|
|
{
|
|
char paraback[1024];
|
|
MessageTag *mtags = NULL;
|
|
|
|
strlcpy(modebuf, parv[3], sizeof modebuf);
|
|
parabuf[0] = '\0';
|
|
if (!nopara)
|
|
{
|
|
for (b = 4; b <= (parc - 2); b++)
|
|
{
|
|
strlcat(parabuf, parv[b], sizeof parabuf);
|
|
strlcat(parabuf, " ", sizeof parabuf);
|
|
}
|
|
}
|
|
strlcpy(paraback, parabuf, sizeof paraback);
|
|
ap = mp2parv(modebuf, parabuf);
|
|
|
|
set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0);
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf);
|
|
}
|
|
|
|
if (merge && !nomode)
|
|
{
|
|
CoreChannelModeTable *acp;
|
|
Mode oldmode; /**< The old mode (OUR mode) */
|
|
|
|
/* Copy current mode to oldmode (need to duplicate all extended mode params too..) */
|
|
memcpy(&oldmode, &channel->mode, sizeof(oldmode));
|
|
memset(&oldmode.extmodeparams, 0, sizeof(oldmode.extmodeparams));
|
|
extcmode_duplicate_paramlist(channel->mode.extmodeparams, oldmode.extmodeparams);
|
|
|
|
/* Now merge the modes */
|
|
strlcpy(modebuf, parv[3], sizeof modebuf);
|
|
parabuf[0] = '\0';
|
|
if (!nopara)
|
|
{
|
|
for (b = 4; b <= (parc - 2); b++)
|
|
{
|
|
strlcat(parabuf, parv[b], sizeof parabuf);
|
|
strlcat(parabuf, " ", sizeof parabuf);
|
|
}
|
|
}
|
|
ap = mp2parv(modebuf, parabuf);
|
|
set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0);
|
|
|
|
/* Good, now we got modes, now for the differencing and outputting of modes
|
|
* We first see if any para modes are set.
|
|
*/
|
|
strlcpy(modebuf, "-", sizeof modebuf);
|
|
parabuf[0] = '\0';
|
|
b = 1;
|
|
/* however, is this really going to happen at all? may be unneeded */
|
|
if (oldmode.limit && !channel->mode.limit)
|
|
{
|
|
Addsingle('l');
|
|
}
|
|
if (oldmode.key[0] && !channel->mode.key[0])
|
|
{
|
|
Addit('k', oldmode.key);
|
|
}
|
|
|
|
/* First, check if we have something they don't have..
|
|
* note that: oldmode.* = us, channel->mode.* = them.
|
|
*/
|
|
for (i=0; i <= Channelmode_highest; i++)
|
|
{
|
|
if (Channelmode_Table[i].flag &&
|
|
!Channelmode_Table[i].local &&
|
|
(oldmode.extmode & Channelmode_Table[i].mode) &&
|
|
!(channel->mode.extmode & Channelmode_Table[i].mode))
|
|
{
|
|
if (Channelmode_Table[i].paracount)
|
|
{
|
|
char *parax = cm_getparameter_ex(oldmode.extmodeparams, Channelmode_Table[i].flag);
|
|
//char *parax = Channelmode_Table[i].get_param(extcmode_get_struct(oldmode.extmodeparam, Channelmode_Table[i].flag));
|
|
Addit(Channelmode_Table[i].flag, parax);
|
|
} else {
|
|
Addsingle(Channelmode_Table[i].flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if we had +s and it became +p, then revert it... */
|
|
if ((oldmode.mode & MODE_SECRET) && (channel->mode.mode & MODE_PRIVATE))
|
|
{
|
|
/* stay +s ! */
|
|
channel->mode.mode &= ~MODE_PRIVATE;
|
|
channel->mode.mode |= MODE_SECRET;
|
|
Addsingle('p'); /* - */
|
|
queue_s = 1;
|
|
}
|
|
/* Add single char modes... */
|
|
for (acp = corechannelmodetable; acp->mode; acp++)
|
|
{
|
|
if ((oldmode.mode & acp->mode) && !(channel->mode.mode & acp->mode) && !acp->parameters)
|
|
{
|
|
Addsingle(acp->flag);
|
|
}
|
|
}
|
|
if (b > 1)
|
|
{
|
|
Addsingle('+');
|
|
}
|
|
else
|
|
{
|
|
strlcpy(modebuf, "+", sizeof modebuf);
|
|
b = 1;
|
|
}
|
|
|
|
if (queue_s)
|
|
Addsingle('s');
|
|
|
|
if (queue_c)
|
|
Addsingle('c');
|
|
|
|
for (acp = corechannelmodetable; acp->mode; acp++)
|
|
{
|
|
if (!(oldmode.mode & acp->mode) && (channel->mode.mode & acp->mode) && !acp->parameters)
|
|
{
|
|
Addsingle(acp->flag);
|
|
}
|
|
}
|
|
|
|
/* Now, check if they have something we don't have..
|
|
* note that: oldmode.* = us, channel->mode.* = them.
|
|
*/
|
|
for (i=0; i <= Channelmode_highest; i++)
|
|
{
|
|
if ((Channelmode_Table[i].flag) &&
|
|
!(oldmode.extmode & Channelmode_Table[i].mode) &&
|
|
(channel->mode.extmode & Channelmode_Table[i].mode))
|
|
{
|
|
if (Channelmode_Table[i].paracount)
|
|
{
|
|
char *parax = cm_getparameter(channel, Channelmode_Table[i].flag);
|
|
if (parax)
|
|
{
|
|
Addit(Channelmode_Table[i].flag, parax);
|
|
}
|
|
} else {
|
|
Addsingle(Channelmode_Table[i].flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now, if we had diffent para modes - this loop really could be done better, but */
|
|
|
|
/* +l (limit) difference? */
|
|
if (oldmode.limit && channel->mode.limit && (oldmode.limit != channel->mode.limit))
|
|
{
|
|
channel->mode.limit = MAX(oldmode.limit, channel->mode.limit);
|
|
if (oldmode.limit != channel->mode.limit)
|
|
{
|
|
Addit('l', my_itoa(channel->mode.limit));
|
|
}
|
|
}
|
|
|
|
/* +k (key) difference? */
|
|
if (oldmode.key[0] && channel->mode.key[0] && strcmp(oldmode.key, channel->mode.key))
|
|
{
|
|
if (strcmp(oldmode.key, channel->mode.key) > 0)
|
|
{
|
|
strlcpy(channel->mode.key, oldmode.key, sizeof channel->mode.key);
|
|
}
|
|
else
|
|
{
|
|
Addit('k', channel->mode.key);
|
|
}
|
|
}
|
|
|
|
/* Now, check for any param differences in extended channel modes..
|
|
* note that: oldmode.* = us, channel->mode.* = them.
|
|
* if we win: copy oldmode to channel mode, if they win: send the mode
|
|
*/
|
|
for (i=0; i <= Channelmode_highest; i++)
|
|
{
|
|
if (Channelmode_Table[i].flag && Channelmode_Table[i].paracount &&
|
|
(oldmode.extmode & Channelmode_Table[i].mode) &&
|
|
(channel->mode.extmode & Channelmode_Table[i].mode))
|
|
{
|
|
int r;
|
|
char *parax;
|
|
char flag = Channelmode_Table[i].flag;
|
|
void *ourm = GETPARASTRUCTEX(oldmode.extmodeparams, flag);
|
|
void *theirm = GETPARASTRUCT(channel, flag);
|
|
//CmodeParam *ourm = extcmode_get_struct(oldmode.extmodeparam,Channelmode_Table[i].flag);
|
|
//CmodeParam *theirm = extcmode_get_struct(channel->mode.extmodeparam, Channelmode_Table[i].flag);
|
|
|
|
r = Channelmode_Table[i].sjoin_check(channel, ourm, theirm);
|
|
switch (r)
|
|
{
|
|
case EXSJ_WEWON:
|
|
parax = cm_getparameter_ex(oldmode.extmodeparams, flag); /* grab from old */
|
|
cm_putparameter(channel, flag, parax); /* put in new (won) */
|
|
break;
|
|
|
|
case EXSJ_THEYWON:
|
|
parax = cm_getparameter(channel, flag);
|
|
Debug((DEBUG_DEBUG, "sjoin: they won: '%s'", parax));
|
|
Addit(Channelmode_Table[i].flag, parax);
|
|
break;
|
|
|
|
case EXSJ_SAME:
|
|
Debug((DEBUG_DEBUG, "sjoin: equal"));
|
|
break;
|
|
|
|
case EXSJ_MERGE:
|
|
parax = cm_getparameter_ex(oldmode.extmodeparams, flag); /* grab from old */
|
|
cm_putparameter(channel, flag, parax); /* put in new (won) */
|
|
Addit(flag, parax);
|
|
break;
|
|
|
|
default:
|
|
ircd_log(LOG_ERROR, "channel.c:m_sjoin:param diff checker: got unk. retval 0x%x??", r);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Addsingle('\0');
|
|
|
|
if (modebuf[1])
|
|
{
|
|
send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf);
|
|
}
|
|
|
|
/* free the oldmode.* crap :( */
|
|
extcmode_free_paramlist(oldmode.extmodeparams);
|
|
/* memset(&oldmode.extmodeparams, 0, sizeof(oldmode.extmodeparams)); -- redundant? */
|
|
}
|
|
|
|
for (h = Hooks[HOOKTYPE_CHANNEL_SYNCED]; h; h = h->next)
|
|
{
|
|
int i = (*(h->func.intfunc))(channel,merge,removetheirs,nomode);
|
|
if (i == 1)
|
|
return; /* channel no longer exists */
|
|
}
|
|
|
|
/* we should be synced by now, */
|
|
if ((oldts != -1) && (oldts != channel->creationtime))
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
new_message(client, NULL, &mtags);
|
|
sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, NULL,
|
|
":%s NOTICE %s :*** TS for %s changed from %lld to %lld",
|
|
me.name, channel->chname, channel->chname,
|
|
(long long)oldts, (long long)channel->creationtime);
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
/* If something went wrong with processing of the SJOIN above and
|
|
* the channel actually has no users in it at this point,
|
|
* then destroy the channel.
|
|
*/
|
|
if (!channel->users)
|
|
{
|
|
sub1_from_channel(channel);
|
|
return;
|
|
}
|
|
}
|