mirror of
https://github.com/matrix-construct/construct
synced 2024-11-04 21:08:57 +01:00
3c7d6fcce7
Also fix up some return values and stuff to use bool (or void if nothing). I just did it whilst I was here. According to jilles, the return value used to signify whether or not the client had exited. This was error-prone and was fixed a long, long time ago, but the return value was left int for historical reasons. Since the return type is not used (and has no clear use case anyway), it's safe to just get rid of it.
388 lines
10 KiB
C
388 lines
10 KiB
C
/* modules/m_services.c
|
|
* Copyright (C) 2005 Lee Hardy <lee -at- leeh.co.uk>
|
|
* Copyright (C) 2005 ircd-ratbox development team
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1.Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2.Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3.The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "stdinc.h"
|
|
|
|
#include "send.h"
|
|
#include "channel.h"
|
|
#include "client.h"
|
|
#include "common.h"
|
|
#include "config.h"
|
|
#include "ircd.h"
|
|
#include "numeric.h"
|
|
#include "s_conf.h"
|
|
#include "s_newconf.h"
|
|
#include "s_serv.h"
|
|
#include "hash.h"
|
|
#include "msg.h"
|
|
#include "parse.h"
|
|
#include "modules.h"
|
|
#include "whowas.h"
|
|
#include "monitor.h"
|
|
#include "supported.h"
|
|
|
|
static const char services_desc[] = "Provides support for running a services daemon";
|
|
|
|
static int _modinit(void);
|
|
static void _moddeinit(void);
|
|
|
|
static void mark_services(void);
|
|
static void unmark_services(void);
|
|
|
|
static void me_su(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
|
static void me_login(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
|
static void me_rsfnc(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
|
static void me_nickdelay(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
|
|
|
static void h_svc_server_introduced(hook_data_client *);
|
|
static void h_svc_whois(hook_data_client *);
|
|
static void h_svc_stats(hook_data_int *);
|
|
static void h_svc_conf_read_start(void *);
|
|
static void h_svc_conf_read_end(void *);
|
|
|
|
struct Message su_msgtab = {
|
|
"SU", 0, 0, 0, 0,
|
|
{mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_su, 2}, mg_ignore}
|
|
};
|
|
struct Message login_msgtab = {
|
|
"LOGIN", 0, 0, 0, 0,
|
|
{mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_login, 2}, mg_ignore}
|
|
};
|
|
struct Message rsfnc_msgtab = {
|
|
"RSFNC", 0, 0, 0, 0,
|
|
{mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_rsfnc, 4}, mg_ignore}
|
|
};
|
|
struct Message nickdelay_msgtab = {
|
|
"NICKDELAY", 0, 0, 0, 0,
|
|
{mg_unreg, mg_ignore, mg_ignore, mg_ignore, {me_nickdelay, 3}, mg_ignore}
|
|
};
|
|
|
|
mapi_clist_av1 services_clist[] = {
|
|
&su_msgtab, &login_msgtab, &rsfnc_msgtab, &nickdelay_msgtab, NULL
|
|
};
|
|
mapi_hfn_list_av1 services_hfnlist[] = {
|
|
{ "doing_stats", (hookfn) h_svc_stats },
|
|
{ "doing_whois", (hookfn) h_svc_whois },
|
|
{ "doing_whois_global", (hookfn) h_svc_whois },
|
|
{ "server_introduced", (hookfn) h_svc_server_introduced },
|
|
{ "conf_read_start", (hookfn) h_svc_conf_read_start },
|
|
{ "conf_read_end", (hookfn) h_svc_conf_read_end },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
DECLARE_MODULE_AV2(services, _modinit, _moddeinit, services_clist, NULL, services_hfnlist, NULL, NULL, services_desc);
|
|
|
|
static int
|
|
_modinit(void)
|
|
{
|
|
mark_services();
|
|
add_isupport("FNC", isupport_string, "");
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_moddeinit(void)
|
|
{
|
|
delete_isupport("FNC");
|
|
unmark_services();
|
|
}
|
|
|
|
static void
|
|
me_su(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
|
|
int parc, const char *parv[])
|
|
{
|
|
struct Client *target_p;
|
|
|
|
if(!(source_p->flags & FLAGS_SERVICE))
|
|
{
|
|
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
|
"Non-service server %s attempting to execute services-only command SU", source_p->name);
|
|
return;
|
|
}
|
|
|
|
if((target_p = find_client(parv[1])) == NULL)
|
|
return;
|
|
|
|
if(!target_p->user)
|
|
return;
|
|
|
|
if(EmptyString(parv[2]))
|
|
target_p->user->suser[0] = '\0';
|
|
else
|
|
rb_strlcpy(target_p->user->suser, parv[2], sizeof(target_p->user->suser));
|
|
|
|
sendto_common_channels_local_butone(target_p, CLICAP_ACCOUNT_NOTIFY, NOCAPS, ":%s!%s@%s ACCOUNT %s",
|
|
target_p->name, target_p->username, target_p->host,
|
|
EmptyString(target_p->user->suser) ? "*" : target_p->user->suser);
|
|
|
|
invalidate_bancache_user(target_p);
|
|
}
|
|
|
|
static void
|
|
me_login(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
|
|
int parc, const char *parv[])
|
|
{
|
|
if(!IsPerson(source_p))
|
|
return;
|
|
|
|
rb_strlcpy(source_p->user->suser, parv[1], sizeof(source_p->user->suser));
|
|
}
|
|
|
|
static void
|
|
me_rsfnc(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
|
|
int parc, const char *parv[])
|
|
{
|
|
struct Client *target_p;
|
|
struct Client *exist_p;
|
|
time_t newts, curts;
|
|
char note[NICKLEN + 10];
|
|
|
|
if(!(source_p->flags & FLAGS_SERVICE))
|
|
{
|
|
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
|
"Non-service server %s attempting to execute services-only command RSFNC", source_p->name);
|
|
return;
|
|
}
|
|
|
|
if((target_p = find_person(parv[1])) == NULL)
|
|
return;
|
|
|
|
if(!MyClient(target_p))
|
|
return;
|
|
|
|
if(!clean_nick(parv[2], 0) || IsDigit(parv[2][0]))
|
|
return;
|
|
|
|
curts = atol(parv[4]);
|
|
|
|
/* if tsinfo is different from what it was when services issued the
|
|
* RSFNC, then we ignore it. This can happen when a client changes
|
|
* nicknames before the RSFNC arrives.. --anfl
|
|
*/
|
|
if(target_p->tsinfo != curts)
|
|
return;
|
|
|
|
if((exist_p = find_named_client(parv[2])))
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
/* this would be one hell of a race condition to trigger
|
|
* this one given the tsinfo check above, but its here for
|
|
* safety --anfl
|
|
*/
|
|
if(target_p == exist_p)
|
|
goto doit;
|
|
|
|
if(MyClient(exist_p))
|
|
sendto_one(exist_p, ":%s KILL %s :(Nickname regained by services)",
|
|
me.name, exist_p->name);
|
|
|
|
exist_p->flags |= FLAGS_KILLED;
|
|
/* Do not send kills to servers for unknowns -- jilles */
|
|
if(IsClient(exist_p))
|
|
{
|
|
kill_client_serv_butone(NULL, exist_p, "%s (Nickname regained by services)",
|
|
me.name);
|
|
sendto_realops_snomask(SNO_SKILL, L_ALL,
|
|
"Nick collision due to services forced nick change on %s",
|
|
parv[2]);
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "Killed (%s (Nickname regained by services))",
|
|
me.name);
|
|
exit_client(NULL, exist_p, &me, buf);
|
|
}
|
|
|
|
doit:
|
|
newts = atol(parv[3]);
|
|
|
|
/* timestamp is older than 15mins, ignore it */
|
|
if(newts < (rb_current_time() - 900))
|
|
newts = rb_current_time() - 900;
|
|
|
|
target_p->tsinfo = newts;
|
|
|
|
monitor_signoff(target_p);
|
|
|
|
invalidate_bancache_user(target_p);
|
|
|
|
sendto_realops_snomask(SNO_NCHANGE, L_ALL,
|
|
"Nick change: From %s to %s [%s@%s]",
|
|
target_p->name, parv[2], target_p->username,
|
|
target_p->host);
|
|
|
|
sendto_common_channels_local(target_p, NOCAPS, NOCAPS, ":%s!%s@%s NICK :%s",
|
|
target_p->name, target_p->username,
|
|
target_p->host, parv[2]);
|
|
|
|
whowas_add_history(target_p, 1);
|
|
sendto_server(NULL, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%ld",
|
|
use_id(target_p), parv[2], (long) target_p->tsinfo);
|
|
|
|
del_from_client_hash(target_p->name, target_p);
|
|
rb_strlcpy(target_p->name, parv[2], NICKLEN);
|
|
add_to_client_hash(target_p->name, target_p);
|
|
|
|
monitor_signon(target_p);
|
|
|
|
del_all_accepts(target_p);
|
|
|
|
snprintf(note, NICKLEN + 10, "Nick: %s", target_p->name);
|
|
rb_note(target_p->localClient->F, note);
|
|
}
|
|
|
|
/*
|
|
** me_nickdelay
|
|
** parv[1] = duration in seconds (0 to remove)
|
|
** parv[2] = nick
|
|
*/
|
|
static void
|
|
me_nickdelay(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
|
{
|
|
int duration;
|
|
struct nd_entry *nd;
|
|
|
|
if(!(source_p->flags & FLAGS_SERVICE))
|
|
{
|
|
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
|
"Non-service server %s attempting to execute services-only command NICKDELAY", source_p->name);
|
|
return;
|
|
}
|
|
|
|
duration = atoi(parv[1]);
|
|
if (duration <= 0)
|
|
{
|
|
nd = rb_dictionary_retrieve(nd_dict, parv[2]);
|
|
if (nd != NULL)
|
|
free_nd_entry(nd);
|
|
}
|
|
else
|
|
{
|
|
if (duration > 86400)
|
|
duration = 86400;
|
|
add_nd_entry(parv[2]);
|
|
nd = rb_dictionary_retrieve(nd_dict, parv[2]);
|
|
if (nd != NULL)
|
|
nd->expire = rb_current_time() + duration;
|
|
}
|
|
}
|
|
|
|
static void
|
|
h_svc_server_introduced(hook_data_client *hdata)
|
|
{
|
|
rb_dlink_node *ptr;
|
|
|
|
RB_DLINK_FOREACH(ptr, service_list.head)
|
|
{
|
|
if(!irccmp((const char *) ptr->data, hdata->target->name))
|
|
{
|
|
hdata->target->flags |= FLAGS_SERVICE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
h_svc_whois(hook_data_client *data)
|
|
{
|
|
char *p = data->target->user->suser;
|
|
if(!EmptyString(p))
|
|
{
|
|
/* Try to strip off any leading digits as this may be used to
|
|
* store both an ID number and an account name in one field.
|
|
* If only digits are present, leave as is.
|
|
*/
|
|
while(IsDigit(*p))
|
|
p++;
|
|
if(*p == '\0')
|
|
p = data->target->user->suser;
|
|
|
|
sendto_one_numeric(data->client, RPL_WHOISLOGGEDIN,
|
|
form_str(RPL_WHOISLOGGEDIN),
|
|
data->target->name, p);
|
|
}
|
|
}
|
|
|
|
static void
|
|
h_svc_stats(hook_data_int *data)
|
|
{
|
|
char statchar = (char) data->arg2;
|
|
rb_dlink_node *ptr;
|
|
|
|
if (statchar == 'U' && IsOper(data->client))
|
|
{
|
|
RB_DLINK_FOREACH(ptr, service_list.head)
|
|
{
|
|
sendto_one_numeric(data->client, RPL_STATSULINE,
|
|
form_str(RPL_STATSULINE),
|
|
(const char *)ptr->data, "*", "*", "s");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
h_svc_conf_read_start(void *dummy)
|
|
{
|
|
unmark_services();
|
|
}
|
|
|
|
static void
|
|
unmark_services(void)
|
|
{
|
|
struct Client *target_p;
|
|
rb_dlink_node *ptr;
|
|
|
|
RB_DLINK_FOREACH(ptr, global_serv_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
target_p->flags &= ~FLAGS_SERVICE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
h_svc_conf_read_end(void *dummy)
|
|
{
|
|
mark_services();
|
|
}
|
|
|
|
static void
|
|
mark_services(void)
|
|
{
|
|
struct Client *target_p;
|
|
rb_dlink_node *ptr;
|
|
|
|
RB_DLINK_FOREACH(ptr, service_list.head)
|
|
{
|
|
target_p = find_server(NULL, (const char *)ptr->data);
|
|
|
|
if (target_p)
|
|
target_p->flags |= FLAGS_SERVICE;
|
|
}
|
|
}
|