mirror of
https://github.com/matrix-construct/construct
synced 2025-03-14 05:20:17 +01:00
ircd: Move tgchange related into namespace.
This commit is contained in:
parent
b85b33d668
commit
3e26e7ab44
11 changed files with 163 additions and 137 deletions
|
@ -164,7 +164,7 @@ m_displaymsg(struct MsgBuf *msgbuf_p, client::client &source, const char *channe
|
|||
return;
|
||||
|
||||
/* enforce target change on roleplay commands */
|
||||
if(!is_chanop(msptr) && !is_voiced(msptr) && !is(source, umode::OPER) && !add_channel_target(&source, chptr))
|
||||
if(!is_chanop(msptr) && !is_voiced(msptr) && !is(source, umode::OPER) && !tgchange::add_target(source, *chptr))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name, source.name, chptr->name.c_str());
|
||||
|
|
|
@ -79,14 +79,6 @@ enum flags
|
|||
EXEMPTJUPE = 0x08000000,
|
||||
};
|
||||
|
||||
enum class tgchange
|
||||
{
|
||||
NUM = 10, // how many targets we keep track of
|
||||
REPLY = 5, // how many reply targets
|
||||
INITIAL = 10, // initial free targets (normal)
|
||||
INITIAL_LOW = 4, // initial free targets (possible spambot)
|
||||
};
|
||||
|
||||
// we store ipv6 ips for remote clients, so this needs to be v6 always
|
||||
constexpr auto PASSWDLEN = 128;
|
||||
constexpr auto CIPHERKEYLEN = 64; // 512bit
|
||||
|
@ -267,7 +259,7 @@ struct LocalUser
|
|||
* 0..TGCHANGE_NUM-1 regular slots
|
||||
* TGCHANGE_NUM..TGCHANGE_NUM+TGCHANGE_REPLY-1 reply slots
|
||||
*/
|
||||
uint32_t targets[int(tgchange::NUM) + int(tgchange::REPLY)];
|
||||
uint32_t targets[tgchange::NUM + tgchange::REPLY];
|
||||
unsigned int targets_free; /* free targets */
|
||||
time_t target_last; /* last time we cleared a slot */
|
||||
|
||||
|
|
|
@ -58,16 +58,8 @@ extern void init_s_newconf(void);
|
|||
extern void clear_s_newconf(void);
|
||||
extern void clear_s_newconf_bans(void);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *ip;
|
||||
time_t expiry;
|
||||
rb_patricia_node_t *pnode;
|
||||
rb_dlink_node node;
|
||||
} tgchange;
|
||||
|
||||
void add_tgchange(const char *host);
|
||||
tgchange *find_tgchange(const char *host);
|
||||
tgchange::tgchange *find_tgchange(const char *host);
|
||||
|
||||
/* shared/cluster/hub/leaf confs */
|
||||
struct remote_conf
|
||||
|
|
|
@ -77,6 +77,7 @@ namespace ircd
|
|||
|
||||
#include "match.h"
|
||||
|
||||
#include "tgchange.h"
|
||||
#include "msgbuf.h"
|
||||
#include "msg.h"
|
||||
#include "client.h"
|
||||
|
@ -122,7 +123,6 @@ namespace ircd
|
|||
#include "substitution.h"
|
||||
#include "supported.h"
|
||||
#include "s_user.h"
|
||||
#include "tgchange.h"
|
||||
#include "whowas.h"
|
||||
#include "wsproc.h"
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2004-2005 Lee Hardy <lee@leeh.co.uk>
|
||||
* Copyright (C) 2005-2010 Jilles Tjoelker <jilles@stack.nl>
|
||||
* Copyright (C) 2004-2005 ircd-ratbox development team
|
||||
* Copyright (C) 2016 Charybdis Development Team
|
||||
*
|
||||
* 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
|
||||
|
@ -25,16 +26,38 @@
|
|||
#define HAVE_IRCD_TGCHANGE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace ircd {
|
||||
namespace ircd {
|
||||
namespace tgchange {
|
||||
|
||||
/* finds a channel where source_p has op or voice and target_p is a member */
|
||||
chan::chan *find_allowing_channel(client::client *source_p, client::client *target_p);
|
||||
/* checks if source_p is allowed to send to target_p */
|
||||
int add_target(client::client *source_p, client::client *target_p);
|
||||
/* checks if source_p is allowed to send to chptr */
|
||||
int add_channel_target(client::client *source_p, chan::chan *chptr);
|
||||
/* allows source_p to send to target_p */
|
||||
void add_reply_target(client::client *source_p, client::client *target_p);
|
||||
using client::client;
|
||||
using chan::chan;
|
||||
|
||||
const auto NUM = 10; // how many targets we keep track of
|
||||
const auto REPLY = 5; // how many reply targets
|
||||
const auto INITIAL = 10; // initial free targets (normal)
|
||||
const auto INITIAL_LOW = 4; // initial free targets (possible spambot)
|
||||
|
||||
struct tgchange
|
||||
{
|
||||
char *ip;
|
||||
time_t expiry;
|
||||
rb_patricia_node_t *pnode;
|
||||
rb_dlink_node node;
|
||||
};
|
||||
|
||||
// finds a channel where source_p has op or voice and target_p is a member
|
||||
const chan *find_allowing_channel(const client &source, const client &target);
|
||||
chan *find_allowing_channel(client &source, const client &target);
|
||||
|
||||
// allows source_p to send to target_p
|
||||
void add_reply_target(client &source, const client &target);
|
||||
|
||||
// checks if source_p is allowed to send to chptr
|
||||
bool add_target(client &source, const chan &target);
|
||||
|
||||
// checks if source_p is allowed to send to target_p
|
||||
bool add_target(client &source, const client &target);
|
||||
|
||||
} // namespace tgchange
|
||||
} // namespace ircd
|
||||
#endif // __cplusplus
|
||||
|
|
|
@ -860,13 +860,13 @@ expire_nd_entries(void *unused)
|
|||
void
|
||||
add_tgchange(const char *host)
|
||||
{
|
||||
tgchange *target;
|
||||
tgchange::tgchange *target;
|
||||
rb_patricia_node_t *pnode;
|
||||
|
||||
if(find_tgchange(host))
|
||||
return;
|
||||
|
||||
target = (tgchange *)rb_malloc(sizeof(tgchange));
|
||||
target = (tgchange::tgchange *)rb_malloc(sizeof(tgchange::tgchange));
|
||||
pnode = make_and_lookup(tgchange_tree, host);
|
||||
|
||||
pnode->data = target;
|
||||
|
@ -878,13 +878,13 @@ add_tgchange(const char *host)
|
|||
rb_dlinkAdd(target, &target->node, &tgchange_list);
|
||||
}
|
||||
|
||||
tgchange *
|
||||
tgchange::tgchange *
|
||||
find_tgchange(const char *host)
|
||||
{
|
||||
rb_patricia_node_t *pnode;
|
||||
|
||||
if((pnode = rb_match_exact_string(tgchange_tree, host)))
|
||||
return (tgchange *)pnode->data;
|
||||
return (tgchange::tgchange *)pnode->data;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -628,9 +628,9 @@ register_local_user(client::client *client_p, client::client *source_p)
|
|||
|
||||
/* they get a reduced limit */
|
||||
if(find_tgchange(source_p->sockhost))
|
||||
source_p->localClient->targets_free = uint(client::tgchange::INITIAL_LOW);
|
||||
source_p->localClient->targets_free = tgchange::INITIAL_LOW;
|
||||
else
|
||||
source_p->localClient->targets_free = uint(client::tgchange::INITIAL);
|
||||
source_p->localClient->targets_free = tgchange::INITIAL;
|
||||
|
||||
monitor_signon(source_p);
|
||||
user_welcome(source_p);
|
||||
|
|
197
ircd/tgchange.cc
197
ircd/tgchange.cc
|
@ -5,6 +5,7 @@
|
|||
* Copyright (C) 2004-2005 Lee Hardy <lee@leeh.co.uk>
|
||||
* Copyright (C) 2005-2010 Jilles Tjoelker <jilles@stack.nl>
|
||||
* Copyright (C) 2004-2005 ircd-ratbox development team
|
||||
* Copyright (C) 2016 Charybdis Development Team
|
||||
*
|
||||
* 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
|
||||
|
@ -22,68 +23,125 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
namespace ircd {
|
||||
namespace ircd {
|
||||
namespace tgchange {
|
||||
|
||||
static int add_hashed_target(client::client *source_p, uint32_t hashv);
|
||||
static bool add_target(client &source, const uint32_t &hashv);
|
||||
|
||||
chan::chan *
|
||||
find_allowing_channel(client::client *source, client::client *target)
|
||||
} // namespace tgchange
|
||||
} // namespace ircd;
|
||||
|
||||
namespace tgchange = ircd::tgchange;
|
||||
using ircd::client::client;
|
||||
using ircd::chan::chan;
|
||||
|
||||
chan *
|
||||
tgchange::find_allowing_channel(client &source,
|
||||
const client &target)
|
||||
{
|
||||
for(const auto &pit : chans(user(*source)))
|
||||
for(const auto &pit : chans(user(source)))
|
||||
{
|
||||
auto &chan(*pit.first);
|
||||
auto &member(*pit.second);
|
||||
|
||||
if ((is_chanop(member) || is_voiced(member)) && is_member(chan, *target))
|
||||
const auto &member(*pit.second);
|
||||
if((is_chanop(member) || is_voiced(member)) && is_member(chan, target))
|
||||
return &chan;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int
|
||||
add_target(client::client *source_p, client::client *target_p)
|
||||
const chan *
|
||||
tgchange::find_allowing_channel(const client &source,
|
||||
const client &target)
|
||||
{
|
||||
uint32_t hashv;
|
||||
for(const auto &pit : chans(user(source)))
|
||||
{
|
||||
const auto &chan(*pit.first);
|
||||
const auto &member(*pit.second);
|
||||
if((is_chanop(member) || is_voiced(member)) && is_member(chan, target))
|
||||
return &chan;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
tgchange::add_target(client &source,
|
||||
const client &target)
|
||||
{
|
||||
/* can msg themselves or services without using any target slots */
|
||||
if(source_p == target_p || is(*target_p, umode::SERVICE))
|
||||
return 1;
|
||||
if(source == target || is(target, umode::SERVICE))
|
||||
return true;
|
||||
|
||||
/* special condition for those who have had PRIVMSG crippled to allow them
|
||||
* to talk to IRCops still.
|
||||
*
|
||||
* XXX: is this controversial?
|
||||
*/
|
||||
if(source_p->localClient->target_last > rb_current_time() && is(*target_p, umode::OPER))
|
||||
return 1;
|
||||
if(source.localClient->target_last > rb_current_time() && is(target, umode::OPER))
|
||||
return true;
|
||||
|
||||
hashv = fnv_hash_upper((const unsigned char *)use_id(target_p), 32);
|
||||
return add_hashed_target(source_p, hashv);
|
||||
const auto hashv(fnv_hash_upper(use_id(target), 32));
|
||||
return add_target(source, hashv);
|
||||
}
|
||||
|
||||
int
|
||||
add_channel_target(client::client *source_p, chan::chan *chptr)
|
||||
bool
|
||||
tgchange::add_target(client &source,
|
||||
const chan &target)
|
||||
{
|
||||
uint32_t hashv;
|
||||
|
||||
if(!ConfigChannel.channel_target_change)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
hashv = fnv_hash_upper((const unsigned char *)chptr->name.c_str(), 32);
|
||||
return add_hashed_target(source_p, hashv);
|
||||
const auto hashv(fnv_hash_upper(name(target).c_str(), 32));
|
||||
return add_target(source, hashv);
|
||||
}
|
||||
|
||||
static int
|
||||
add_hashed_target(client::client *source_p, uint32_t hashv)
|
||||
void
|
||||
tgchange::add_reply_target(client &source,
|
||||
const client &target)
|
||||
{
|
||||
/* can msg themselves or services without using any target slots */
|
||||
if(source == target || is(target, umode::SERVICE))
|
||||
return;
|
||||
|
||||
auto &targets(source.localClient->targets);
|
||||
static_assert(NUM + REPLY == sizeof(targets) / sizeof(uint32_t), "");
|
||||
|
||||
/* check for existing target, and move it to the first reply slot
|
||||
* if it is in a reply slot
|
||||
*/
|
||||
const auto hashv(fnv_hash_upper(use_id(target), 32));
|
||||
for(int i(0); i < NUM + REPLY; i++)
|
||||
{
|
||||
if(targets[i] == hashv)
|
||||
{
|
||||
if(i > NUM)
|
||||
{
|
||||
for(int j(i); j > NUM; j--)
|
||||
targets[j] = targets[j - 1];
|
||||
targets[NUM] = hashv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i(NUM + REPLY - 1); i > NUM; i--)
|
||||
targets[i] = targets[i - 1];
|
||||
targets[NUM] = hashv;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
tgchange::add_target(client &source,
|
||||
const uint32_t &hashv)
|
||||
{
|
||||
int i, j;
|
||||
uint32_t *targets;
|
||||
|
||||
targets = source_p->localClient->targets;
|
||||
targets = source.localClient->targets;
|
||||
|
||||
/* check for existing target, and move it to the head */
|
||||
for(i = 0; i < int(client::tgchange::NUM) + int(client::tgchange::REPLY); i++)
|
||||
for(i = 0; i < NUM + REPLY; i++)
|
||||
{
|
||||
if(targets[i] == hashv)
|
||||
{
|
||||
|
@ -94,48 +152,46 @@ add_hashed_target(client::client *source_p, uint32_t hashv)
|
|||
}
|
||||
}
|
||||
|
||||
if(source_p->localClient->targets_free < int(client::tgchange::NUM))
|
||||
if(source.localClient->targets_free < NUM)
|
||||
{
|
||||
/* first message after connect, we may only start clearing
|
||||
* slots after this message --anfl
|
||||
*/
|
||||
if(!is_tg_change(*source_p))
|
||||
if(!is_tg_change(source))
|
||||
{
|
||||
set_tg_change(*source_p);
|
||||
source_p->localClient->target_last = rb_current_time();
|
||||
set_tg_change(source);
|
||||
source.localClient->target_last = rb_current_time();
|
||||
}
|
||||
/* clear as many targets as we can */
|
||||
else if((i = (rb_current_time() - source_p->localClient->target_last) / 60))
|
||||
else if((i = (rb_current_time() - source.localClient->target_last) / 60))
|
||||
{
|
||||
if(i + source_p->localClient->targets_free > int(client::tgchange::NUM))
|
||||
source_p->localClient->targets_free = int(client::tgchange::NUM);
|
||||
if(i + source.localClient->targets_free > NUM)
|
||||
source.localClient->targets_free = NUM;
|
||||
else
|
||||
source_p->localClient->targets_free += i;
|
||||
source.localClient->targets_free += i;
|
||||
|
||||
source_p->localClient->target_last = rb_current_time();
|
||||
source.localClient->target_last = rb_current_time();
|
||||
}
|
||||
/* cant clear any, full target list */
|
||||
else if(source_p->localClient->targets_free == 0)
|
||||
else if(source.localClient->targets_free == 0)
|
||||
{
|
||||
ServerStats.is_tgch++;
|
||||
add_tgchange(source_p->sockhost);
|
||||
add_tgchange(source.sockhost);
|
||||
|
||||
if (!is_tg_excessive(*source_p))
|
||||
if (!is_tg_excessive(source))
|
||||
{
|
||||
set_tg_excessive(*source_p);
|
||||
set_tg_excessive(source);
|
||||
/* This is sent to L_ALL because it's regenerated on all servers
|
||||
* that have the TGINFO module loaded.
|
||||
*/
|
||||
sendto_realops_snomask(SNO_BOTS, L_ALL,
|
||||
"Excessive target change from %s (%s@%s)",
|
||||
source_p->name, source_p->username,
|
||||
source_p->orighost);
|
||||
source.name, source.username,
|
||||
source.orighost);
|
||||
}
|
||||
|
||||
sendto_match_servs(source_p, "*", CAP_ENCAP, NOCAPS,
|
||||
"ENCAP * TGINFO 0");
|
||||
|
||||
return 0;
|
||||
sendto_match_servs(&source, "*", CAP_ENCAP, NOCAPS, "ENCAP * TGINFO 0");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* no targets in use, reset their target_last so that they cant
|
||||
|
@ -143,50 +199,13 @@ add_hashed_target(client::client *source_p, uint32_t hashv)
|
|||
*/
|
||||
else
|
||||
{
|
||||
source_p->localClient->target_last = rb_current_time();
|
||||
set_tg_change(*source_p);
|
||||
source.localClient->target_last = rb_current_time();
|
||||
set_tg_change(source);
|
||||
}
|
||||
|
||||
for(i = int(client::tgchange::NUM) + int(client::tgchange::REPLY) - 1; i > 0; i--)
|
||||
for(i = NUM + REPLY - 1; i > 0; i--)
|
||||
targets[i] = targets[i - 1];
|
||||
targets[0] = hashv;
|
||||
source_p->localClient->targets_free--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
add_reply_target(client::client *source_p, client::client *target_p)
|
||||
{
|
||||
int i, j;
|
||||
uint32_t hashv;
|
||||
uint32_t *targets;
|
||||
|
||||
/* can msg themselves or services without using any target slots */
|
||||
if(source_p == target_p || is(*target_p, umode::SERVICE))
|
||||
return;
|
||||
|
||||
hashv = fnv_hash_upper((const unsigned char *)use_id(target_p), 32);
|
||||
targets = source_p->localClient->targets;
|
||||
|
||||
/* check for existing target, and move it to the first reply slot
|
||||
* if it is in a reply slot
|
||||
*/
|
||||
for(i = 0; i < int(client::tgchange::NUM) + int(client::tgchange::REPLY); i++)
|
||||
{
|
||||
if(targets[i] == hashv)
|
||||
{
|
||||
if(i > int(client::tgchange::NUM))
|
||||
{
|
||||
for(j = i; j > int(client::tgchange::NUM); j--)
|
||||
targets[j] = targets[j - 1];
|
||||
targets[int(client::tgchange::NUM)] = hashv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(i = int(client::tgchange::NUM) + int(client::tgchange::REPLY) - 1; i > int(client::tgchange::NUM); i--)
|
||||
targets[i] = targets[i - 1];
|
||||
targets[int(client::tgchange::NUM)] = hashv;
|
||||
}
|
||||
|
||||
source.localClient->targets_free--;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -513,7 +513,7 @@ msg_channel(enum message_type msgtype,
|
|||
{
|
||||
if(result != chan::CAN_SEND_OPV && my(source) &&
|
||||
!is(source, umode::OPER) &&
|
||||
!add_channel_target(&source, chptr))
|
||||
!tgchange::add_target(source, *chptr))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name,
|
||||
|
@ -534,7 +534,7 @@ msg_channel(enum message_type msgtype,
|
|||
else if(chptr->mode.mode & chan::mode::OPMODERATE &&
|
||||
(!(chptr->mode.mode & chan::mode::NOPRIVMSGS) || is_member(chptr, &source)))
|
||||
{
|
||||
if(my(source) && !is(source, umode::OPER) && !add_channel_target(&source, chptr))
|
||||
if(my(source) && !is(source, umode::OPER) && !tgchange::add_target(source, *chptr))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name,
|
||||
|
@ -683,12 +683,12 @@ msg_channel_flags(enum message_type msgtype, client::client &client,
|
|||
static void
|
||||
expire_tgchange(void *unused)
|
||||
{
|
||||
tgchange *target;
|
||||
tgchange::tgchange *target;
|
||||
rb_dlink_node *ptr, *next_ptr;
|
||||
|
||||
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
|
||||
{
|
||||
target = (tgchange *)ptr->data;
|
||||
target = (tgchange::tgchange *)ptr->data;
|
||||
|
||||
if(target->expiry < rb_current_time())
|
||||
{
|
||||
|
@ -754,7 +754,7 @@ msg_client(enum message_type msgtype,
|
|||
|
||||
/* auto cprivmsg/cnotice */
|
||||
do_floodcount = !is(source, umode::OPER) &&
|
||||
!find_allowing_channel(&source, target_p);
|
||||
!tgchange::find_allowing_channel(source, *target_p);
|
||||
|
||||
/* target change stuff, dont limit ctcp replies as that
|
||||
* would allow people to start filling up random users
|
||||
|
@ -763,7 +763,7 @@ msg_client(enum message_type msgtype,
|
|||
if((msgtype != MESSAGE_TYPE_NOTICE || *text != '\001') &&
|
||||
ConfigFileEntry.target_change && do_floodcount)
|
||||
{
|
||||
if(!add_target(&source, target_p))
|
||||
if(!tgchange::add_target(source, *target_p))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name, source.name, target_p->name);
|
||||
|
@ -823,7 +823,7 @@ msg_client(enum message_type msgtype,
|
|||
/* Here is the anti-flood bot/spambot code -db */
|
||||
if(accept_message(&source, target_p) || is(source, umode::OPER))
|
||||
{
|
||||
add_reply_target(target_p, &source);
|
||||
tgchange::add_reply_target(*target_p, source);
|
||||
sendto_one(target_p, ":%s!%s@%s %s %s :%s",
|
||||
source.name,
|
||||
source.username,
|
||||
|
@ -854,7 +854,7 @@ msg_client(enum message_type msgtype,
|
|||
form_str(RPL_TARGNOTIFY),
|
||||
target_p->name);
|
||||
|
||||
add_reply_target(target_p, &source);
|
||||
tgchange::add_reply_target(*target_p, source);
|
||||
sendto_one(target_p, form_str(RPL_UMODEGMSG),
|
||||
me.name, target_p->name, source.name,
|
||||
source.username, source.host);
|
||||
|
@ -865,7 +865,7 @@ msg_client(enum message_type msgtype,
|
|||
}
|
||||
else
|
||||
{
|
||||
add_reply_target(target_p, &source);
|
||||
tgchange::add_reply_target(*target_p, source);
|
||||
sendto_anywhere(target_p, &source, cmdname[msgtype], ":%s", text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,8 +149,8 @@ m_invite(struct MsgBuf *msgbuf_p, client::client &client, client::client &source
|
|||
if(my_connect(source))
|
||||
{
|
||||
if (ConfigFileEntry.target_change && !is(source, umode::OPER) &&
|
||||
!find_allowing_channel(&source, target_p) &&
|
||||
!add_target(&source, target_p))
|
||||
!tgchange::find_allowing_channel(source, *target_p) &&
|
||||
!tgchange::add_target(source, *target_p))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name, source.name, target_p->name);
|
||||
|
@ -200,7 +200,7 @@ m_invite(struct MsgBuf *msgbuf_p, client::client &client, client::client &source
|
|||
target_p->localClient->last_caller_id_time = rb_current_time();
|
||||
}
|
||||
}
|
||||
add_reply_target(target_p, &source);
|
||||
tgchange::add_reply_target(*target_p, source);
|
||||
sendto_one(target_p, ":%s!%s@%s INVITE %s :%s",
|
||||
source.name, source.username, source.host,
|
||||
target_p->name, chptr->name.c_str());
|
||||
|
|
|
@ -98,7 +98,7 @@ m_topic(struct MsgBuf *msgbuf_p, client::client &client, client::client &source,
|
|||
!is_chanop(msptr) &&
|
||||
!is_voiced(msptr) &&
|
||||
!is(source, umode::OPER) &&
|
||||
!add_channel_target(&source, chptr))
|
||||
!tgchange::add_target(source, *chptr))
|
||||
{
|
||||
sendto_one(&source, form_str(ERR_TARGCHANGE),
|
||||
me.name, source.name, chptr->name.c_str());
|
||||
|
|
Loading…
Add table
Reference in a new issue