2001-12-14 09:13:43 +01:00
|
|
|
/*
|
|
|
|
* ngIRCd -- The Next Generation IRC Daemon
|
2015-09-03 16:22:36 +02:00
|
|
|
* Copyright (c)2001-2015 Alexander Barton (alex@barton.de) and Contributors.
|
2001-12-14 09:13:43 +01:00
|
|
|
*
|
2002-12-12 13:24:18 +01:00
|
|
|
* 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* Please read the file COPYING, README and AUTHORS for more information.
|
2002-02-28 00:26:36 +01:00
|
|
|
*/
|
2001-12-26 04:21:46 +01:00
|
|
|
|
2002-03-12 15:37:51 +01:00
|
|
|
#include "portab.h"
|
2002-02-11 16:15:53 +01:00
|
|
|
|
2010-12-27 17:14:14 +01:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* IRC commands
|
|
|
|
*/
|
2002-12-12 13:24:18 +01:00
|
|
|
|
2002-02-28 00:26:36 +01:00
|
|
|
#include <assert.h>
|
2002-12-12 12:40:41 +01:00
|
|
|
#include <stdio.h>
|
2002-02-28 00:26:36 +01:00
|
|
|
#include <string.h>
|
2014-03-17 02:28:39 +01:00
|
|
|
#include <strings.h>
|
|
|
|
#include <time.h>
|
2001-12-26 04:21:46 +01:00
|
|
|
|
2003-01-13 19:56:30 +01:00
|
|
|
#include "ngircd.h"
|
2002-12-30 18:15:42 +01:00
|
|
|
#include "conn-func.h"
|
2006-07-23 16:55:40 +02:00
|
|
|
#include "conf.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "channel.h"
|
2014-03-17 02:28:39 +01:00
|
|
|
#ifdef ICONV
|
|
|
|
# include "conn-encoding.h"
|
|
|
|
#endif
|
2013-07-18 23:35:33 +02:00
|
|
|
#include "irc-macros.h"
|
2002-02-28 00:26:36 +01:00
|
|
|
#include "irc-write.h"
|
|
|
|
#include "log.h"
|
2008-06-11 20:35:50 +02:00
|
|
|
#include "match.h"
|
2002-02-28 00:26:36 +01:00
|
|
|
#include "messages.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "parse.h"
|
2013-09-25 01:29:23 +02:00
|
|
|
#include "op.h"
|
2002-02-11 02:03:20 +01:00
|
|
|
|
2002-02-28 00:26:36 +01:00
|
|
|
#include "irc.h"
|
2001-12-26 04:21:46 +01:00
|
|
|
|
2008-05-05 18:06:43 +02:00
|
|
|
static char *Option_String PARAMS((CONN_ID Idx));
|
2008-08-16 18:26:19 +02:00
|
|
|
static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
|
|
|
|
bool SendErrors));
|
|
|
|
static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
|
|
|
|
char *targetMask, char *message,
|
|
|
|
bool SendErrors));
|
2013-01-01 14:57:03 +01:00
|
|
|
static bool Help PARAMS((CLIENT *Client, const char *Topic));
|
2003-03-19 22:16:53 +01:00
|
|
|
|
2012-01-06 20:05:07 +01:00
|
|
|
/**
|
|
|
|
* Check if a list limit is reached and inform client accordingly.
|
|
|
|
*
|
|
|
|
* @param From The client.
|
|
|
|
* @param Count Reply item count.
|
|
|
|
* @param Limit Reply limit.
|
|
|
|
* @param Name Name of the list.
|
|
|
|
* @return true if list limit has been reached; false otherwise.
|
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_CheckListTooBig(CLIENT *From, const int Count, const int Limit,
|
|
|
|
const char *Name)
|
|
|
|
{
|
|
|
|
assert(From != NULL);
|
|
|
|
assert(Count >= 0);
|
|
|
|
assert(Limit > 0);
|
|
|
|
assert(Name != NULL);
|
|
|
|
|
|
|
|
if (Count < Limit)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
(void)IRC_WriteStrClient(From,
|
|
|
|
"NOTICE %s :%s list limit (%d) reached!",
|
|
|
|
Client_ID(From), Name, Limit);
|
|
|
|
IRC_SetPenalty(From, 2);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-18 23:35:33 +02:00
|
|
|
/**
|
|
|
|
* Handler for the IRC "ERROR" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2013-07-18 23:35:33 +02:00
|
|
|
IRC_ERROR(CLIENT *Client, REQUEST *Req)
|
2002-01-02 03:44:36 +01:00
|
|
|
{
|
|
|
|
assert( Client != NULL );
|
|
|
|
assert( Req != NULL );
|
|
|
|
|
2012-12-31 21:46:41 +01:00
|
|
|
if (Client_Type(Client) != CLIENT_GOTPASS
|
|
|
|
&& Client_Type(Client) != CLIENT_GOTPASS_2813
|
|
|
|
&& Client_Type(Client) != CLIENT_UNKNOWNSERVER
|
|
|
|
&& Client_Type(Client) != CLIENT_SERVER
|
|
|
|
&& Client_Type(Client) != CLIENT_SERVICE) {
|
|
|
|
LogDebug("Ignored ERROR command from \"%s\" ...",
|
|
|
|
Client_Mask(Client));
|
|
|
|
IRC_SetPenalty(Client, 2);
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2010-01-19 19:20:56 +01:00
|
|
|
if (Req->argc < 1)
|
|
|
|
Log(LOG_NOTICE, "Got ERROR from \"%s\"!",
|
|
|
|
Client_Mask(Client));
|
|
|
|
else
|
|
|
|
Log(LOG_NOTICE, "Got ERROR from \"%s\": \"%s\"!",
|
|
|
|
Client_Mask(Client), Req->argv[0]);
|
2002-01-02 03:44:36 +01:00
|
|
|
|
|
|
|
return CONNECTED;
|
|
|
|
} /* IRC_ERROR */
|
2001-12-31 03:18:51 +01:00
|
|
|
|
2005-04-18 17:44:39 +02:00
|
|
|
/**
|
2011-07-30 19:47:59 +02:00
|
|
|
* Handler for the IRC "KILL" command.
|
|
|
|
*
|
2013-08-04 13:33:10 +02:00
|
|
|
* This function implements the IRC command "KILL" which is used to selectively
|
2005-04-18 17:44:39 +02:00
|
|
|
* disconnect clients. It can be used by IRC operators and servers, for example
|
2011-07-30 19:47:59 +02:00
|
|
|
* to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
|
|
|
|
*
|
2005-04-18 17:44:39 +02:00
|
|
|
* Please note that this function is also called internally, without a real
|
2010-05-14 19:43:08 +02:00
|
|
|
* KILL command being received over the network! Client is Client_ThisServer()
|
2011-07-30 19:47:59 +02:00
|
|
|
* in this case, and the prefix in Req is NULL.
|
|
|
|
*
|
2013-07-18 23:35:33 +02:00
|
|
|
* @param Client The client from which this command has been received or
|
|
|
|
* Client_ThisServer() when generated interanlly.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
2011-07-30 19:47:59 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2013-07-18 23:35:33 +02:00
|
|
|
IRC_KILL(CLIENT *Client, REQUEST *Req)
|
2002-02-23 22:39:48 +01:00
|
|
|
{
|
2013-10-01 12:13:17 +02:00
|
|
|
CLIENT *prefix;
|
|
|
|
char reason[COMMAND_LEN];
|
2002-11-30 18:39:56 +01:00
|
|
|
|
2011-07-30 19:47:59 +02:00
|
|
|
assert (Client != NULL);
|
|
|
|
assert (Req != NULL);
|
2002-02-23 22:39:48 +01:00
|
|
|
|
2013-09-25 01:29:23 +02:00
|
|
|
if (Client_Type(Client) != CLIENT_SERVER && !Op_Check(Client, Req))
|
|
|
|
return Op_NoPrivileges(Client, Req);
|
2002-12-06 18:02:39 +01:00
|
|
|
|
2011-07-30 19:47:59 +02:00
|
|
|
/* Get prefix (origin); use the client if no prefix is given. */
|
|
|
|
if (Req->prefix)
|
|
|
|
prefix = Client_Search(Req->prefix);
|
|
|
|
else
|
|
|
|
prefix = Client;
|
|
|
|
|
|
|
|
/* Log a warning message and use this server as origin when the
|
2013-07-18 23:35:33 +02:00
|
|
|
* prefix (origin) is invalid. And this is the reason why we don't
|
|
|
|
* use the _IRC_GET_SENDER_OR_RETURN_ macro above! */
|
2011-07-30 19:47:59 +02:00
|
|
|
if (!prefix) {
|
|
|
|
Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
|
|
|
|
Req->prefix );
|
|
|
|
prefix = Client_ThisServer();
|
2002-02-23 22:39:48 +01:00
|
|
|
}
|
2002-11-30 18:39:56 +01:00
|
|
|
|
2011-07-30 19:47:59 +02:00
|
|
|
if (Client != Client_ThisServer())
|
|
|
|
Log(LOG_NOTICE|LOG_snotice,
|
2013-01-26 16:52:41 +01:00
|
|
|
"Got KILL command from \"%s\" for \"%s\": \"%s\".",
|
2011-07-30 19:47:59 +02:00
|
|
|
Client_Mask(prefix), Req->argv[0], Req->argv[1]);
|
2002-12-06 18:02:39 +01:00
|
|
|
|
2011-07-30 19:47:59 +02:00
|
|
|
/* Build reason string: Prefix the "reason" if the originator is a
|
|
|
|
* regular user, so users can't spoof KILLs of servers. */
|
|
|
|
if (Client_Type(Client) == CLIENT_USER)
|
|
|
|
snprintf(reason, sizeof(reason), "KILLed by %s: %s",
|
|
|
|
Client_ID(Client), Req->argv[1]);
|
2005-04-18 17:44:39 +02:00
|
|
|
else
|
2011-07-30 19:47:59 +02:00
|
|
|
strlcpy(reason, Req->argv[1], sizeof(reason));
|
2002-11-30 18:39:56 +01:00
|
|
|
|
2013-10-01 12:13:17 +02:00
|
|
|
return IRC_KillClient(Client, prefix, Req->argv[0], reason);
|
|
|
|
}
|
2002-02-23 22:39:48 +01:00
|
|
|
|
2008-07-27 15:50:51 +02:00
|
|
|
/**
|
2013-07-18 23:35:33 +02:00
|
|
|
* Handler for the IRC "NOTICE" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2008-07-27 15:50:51 +02:00
|
|
|
IRC_NOTICE(CLIENT *Client, REQUEST *Req)
|
2002-09-16 11:14:45 +02:00
|
|
|
{
|
2008-06-11 20:35:50 +02:00
|
|
|
return Send_Message(Client, Req, CLIENT_USER, false);
|
2002-11-30 18:39:56 +01:00
|
|
|
} /* IRC_NOTICE */
|
2002-02-23 22:39:48 +01:00
|
|
|
|
2008-05-05 18:06:43 +02:00
|
|
|
/**
|
2013-07-18 23:35:33 +02:00
|
|
|
* Handler for the IRC "PRIVMSG" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
2008-05-05 18:06:43 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2008-05-05 18:06:43 +02:00
|
|
|
IRC_PRIVMSG(CLIENT *Client, REQUEST *Req)
|
2002-01-28 14:05:48 +01:00
|
|
|
{
|
2008-06-11 20:35:50 +02:00
|
|
|
return Send_Message(Client, Req, CLIENT_USER, true);
|
2008-05-05 18:06:43 +02:00
|
|
|
} /* IRC_PRIVMSG */
|
2002-02-14 00:05:29 +01:00
|
|
|
|
2008-05-05 18:06:43 +02:00
|
|
|
/**
|
2013-07-18 23:35:33 +02:00
|
|
|
* Handler for the IRC "SQUERY" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
2008-05-05 18:06:43 +02:00
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_SQUERY(CLIENT *Client, REQUEST *Req)
|
|
|
|
{
|
2008-06-11 20:35:50 +02:00
|
|
|
return Send_Message(Client, Req, CLIENT_SERVICE, true);
|
2008-05-05 18:06:43 +02:00
|
|
|
} /* IRC_SQUERY */
|
2002-02-25 18:46:27 +01:00
|
|
|
|
2013-07-18 23:35:33 +02:00
|
|
|
/*
|
|
|
|
* Handler for the IRC "TRACE" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_TRACE(CLIENT *Client, REQUEST *Req)
|
2003-01-13 19:56:30 +01:00
|
|
|
{
|
2003-03-19 22:16:53 +01:00
|
|
|
CLIENT *from, *target, *c;
|
2003-01-13 19:56:30 +01:00
|
|
|
CONN_ID idx, idx2;
|
2005-03-19 19:43:48 +01:00
|
|
|
char user[CLIENT_USER_LEN];
|
2003-01-13 19:56:30 +01:00
|
|
|
|
2013-07-18 23:35:33 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
|
|
|
|
|
|
|
_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
|
|
|
|
_IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
|
2003-01-13 19:56:30 +01:00
|
|
|
|
|
|
|
/* Forward command to other server? */
|
2013-07-18 23:35:33 +02:00
|
|
|
if (target != Client_ThisServer()) {
|
2003-01-13 19:56:30 +01:00
|
|
|
/* Send RPL_TRACELINK back to initiator */
|
2013-07-18 23:35:33 +02:00
|
|
|
idx = Client_Conn(Client);
|
|
|
|
assert(idx > NONE);
|
|
|
|
idx2 = Client_Conn(Client_NextHop(target));
|
|
|
|
assert(idx2 > NONE);
|
|
|
|
|
|
|
|
if (!IRC_WriteStrClient(from, RPL_TRACELINK_MSG,
|
|
|
|
Client_ID(from), PACKAGE_NAME,
|
|
|
|
PACKAGE_VERSION, Client_ID(target),
|
|
|
|
Client_ID(Client_NextHop(target)),
|
|
|
|
Option_String(idx2),
|
2015-11-15 15:14:12 +01:00
|
|
|
(long)(time(NULL) - Conn_StartTime(idx2)),
|
2013-07-18 23:35:33 +02:00
|
|
|
Conn_SendQ(idx), Conn_SendQ(idx2)))
|
|
|
|
return DISCONNECTED;
|
2003-01-13 19:56:30 +01:00
|
|
|
|
|
|
|
/* Forward command */
|
2013-07-18 23:35:33 +02:00
|
|
|
IRC_WriteStrClientPrefix(target, from, "TRACE %s", Req->argv[0]);
|
2003-01-13 19:56:30 +01:00
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2003-03-19 22:16:53 +01:00
|
|
|
/* Infos about all connected servers */
|
2013-07-18 23:35:33 +02:00
|
|
|
c = Client_First();
|
|
|
|
while (c) {
|
|
|
|
if (Client_Conn(c) > NONE) {
|
2003-03-19 22:16:53 +01:00
|
|
|
/* Local client */
|
2013-07-18 23:35:33 +02:00
|
|
|
if (Client_Type(c) == CLIENT_SERVER) {
|
2003-03-19 22:16:53 +01:00
|
|
|
/* Server link */
|
2013-07-18 23:35:33 +02:00
|
|
|
strlcpy(user, Client_User(c), sizeof(user));
|
|
|
|
if (user[0] == '~')
|
|
|
|
strlcpy(user, "unknown", sizeof(user));
|
|
|
|
if (!IRC_WriteStrClient(from,
|
|
|
|
RPL_TRACESERVER_MSG,
|
|
|
|
Client_ID(from), Client_ID(c),
|
|
|
|
user, Client_Hostname(c),
|
|
|
|
Client_Mask(Client_ThisServer()),
|
|
|
|
Option_String(Client_Conn(c))))
|
|
|
|
return DISCONNECTED;
|
2003-03-19 22:16:53 +01:00
|
|
|
}
|
2013-07-18 23:35:33 +02:00
|
|
|
if (Client_Type(c) == CLIENT_USER
|
2013-08-04 19:28:04 +02:00
|
|
|
&& Client_HasMode(c, 'o')) {
|
2003-03-19 22:16:53 +01:00
|
|
|
/* IRC Operator */
|
2013-07-18 23:35:33 +02:00
|
|
|
if (!IRC_WriteStrClient(from,
|
|
|
|
RPL_TRACEOPERATOR_MSG,
|
|
|
|
Client_ID(from), Client_ID(c)))
|
|
|
|
return DISCONNECTED;
|
2003-03-19 22:16:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
c = Client_Next( c );
|
|
|
|
}
|
|
|
|
|
2013-07-18 23:35:33 +02:00
|
|
|
return IRC_WriteStrClient(from, RPL_TRACEEND_MSG, Client_ID(from),
|
|
|
|
Conf_ServerName, PACKAGE_NAME,
|
|
|
|
PACKAGE_VERSION, NGIRCd_DebugLevel);
|
2003-01-13 19:56:30 +01:00
|
|
|
} /* IRC_TRACE */
|
|
|
|
|
2012-12-31 19:27:32 +01:00
|
|
|
/**
|
|
|
|
* Handler for the IRC "HELP" command.
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2012-12-31 19:27:32 +01:00
|
|
|
IRC_HELP(CLIENT *Client, REQUEST *Req)
|
2003-01-15 14:49:20 +01:00
|
|
|
{
|
|
|
|
COMMAND *cmd;
|
|
|
|
|
2012-12-31 19:27:32 +01:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
2003-01-15 14:49:20 +01:00
|
|
|
|
2013-01-01 14:57:03 +01:00
|
|
|
if ((Req->argc == 0 && array_bytes(&Conf_Helptext) > 0)
|
|
|
|
|| (Req->argc >= 1 && strcasecmp(Req->argv[0], "Commands") != 0)) {
|
|
|
|
/* Help text available and requested */
|
|
|
|
if (Req->argc >= 1)
|
|
|
|
return Help(Client, Req->argv[0]);
|
|
|
|
|
|
|
|
if (!Help(Client, "Intro"))
|
|
|
|
return DISCONNECTED;
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2012-12-31 19:27:32 +01:00
|
|
|
cmd = Parse_GetCommandStruct();
|
|
|
|
while(cmd->name) {
|
|
|
|
if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
|
|
|
|
Client_ID(Client), cmd->name))
|
|
|
|
return DISCONNECTED;
|
2003-01-15 14:49:20 +01:00
|
|
|
cmd++;
|
|
|
|
}
|
|
|
|
return CONNECTED;
|
|
|
|
} /* IRC_HELP */
|
|
|
|
|
2013-10-01 12:13:17 +02:00
|
|
|
/**
|
|
|
|
* Kill an client identified by its nick name.
|
|
|
|
*
|
|
|
|
* Please note that after killig a client, its CLIENT cond CONNECTION
|
|
|
|
* structures are invalid. So the caller must make sure on its own not to
|
|
|
|
* access data of probably killed clients after calling this function!
|
|
|
|
*
|
|
|
|
* @param Client The client from which the command leading to the KILL has
|
|
|
|
* been received, or NULL. The KILL will no be forwarded in this
|
|
|
|
* direction. Only relevant when From is set, too.
|
|
|
|
* @param From The client from which the command originated, or NULL for
|
|
|
|
the local server.
|
|
|
|
* @param Nick The nick name to kill.
|
|
|
|
* @param Reason Text to send as reason to the client and other servers.
|
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_KillClient(CLIENT *Client, CLIENT *From, const char *Nick, const char *Reason)
|
|
|
|
{
|
|
|
|
const char *msg;
|
2015-12-13 21:48:31 +01:00
|
|
|
CONN_ID my_conn = NONE, conn;
|
2013-10-01 12:13:17 +02:00
|
|
|
CLIENT *c;
|
|
|
|
|
2015-12-13 21:48:31 +01:00
|
|
|
assert(Nick != NULL);
|
|
|
|
assert(Reason != NULL);
|
|
|
|
|
2013-09-26 02:58:01 +02:00
|
|
|
/* Do we know such a client in the network? */
|
2013-10-01 12:13:17 +02:00
|
|
|
c = Client_Search(Nick);
|
|
|
|
if (!c) {
|
2013-09-26 02:58:01 +02:00
|
|
|
LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick);
|
2013-10-01 12:13:17 +02:00
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2013-09-26 02:58:01 +02:00
|
|
|
/* Inform other servers */
|
|
|
|
IRC_WriteStrServersPrefix(From ? Client : NULL,
|
|
|
|
From ? From : Client_ThisServer(),
|
|
|
|
"KILL %s :%s", Nick, Reason);
|
|
|
|
|
2013-10-01 12:13:17 +02:00
|
|
|
if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK) {
|
|
|
|
/* Target of this KILL is not a regular user, this is
|
|
|
|
* invalid! So we ignore this case if we received a
|
|
|
|
* regular KILL from the network and try to kill the
|
|
|
|
* client/connection anyway (but log an error!) if the
|
|
|
|
* origin is the local server. */
|
|
|
|
|
|
|
|
if (Client != Client_ThisServer()) {
|
|
|
|
/* Invalid KILL received from remote */
|
|
|
|
if (Client_Type(c) == CLIENT_SERVER)
|
|
|
|
msg = ERR_CANTKILLSERVER_MSG;
|
|
|
|
else
|
|
|
|
msg = ERR_NOPRIVILEGES_MSG;
|
|
|
|
return IRC_WriteErrClient(Client, msg, Client_ID(Client));
|
|
|
|
}
|
|
|
|
|
|
|
|
Log(LOG_ERR,
|
|
|
|
"Got KILL for invalid client type: %d, \"%s\"!",
|
|
|
|
Client_Type(c), Nick);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save ID of this connection */
|
2015-12-13 21:48:31 +01:00
|
|
|
if (Client)
|
|
|
|
my_conn = Client_Conn(Client);
|
2013-10-01 12:13:17 +02:00
|
|
|
|
|
|
|
/* Kill the client NOW:
|
|
|
|
* - Close the local connection (if there is one),
|
|
|
|
* - Destroy the CLIENT structure for remote clients.
|
|
|
|
* Note: Conn_Close() removes the CLIENT structure as well. */
|
|
|
|
conn = Client_Conn(c);
|
|
|
|
if(conn > NONE)
|
|
|
|
Conn_Close(conn, NULL, Reason, true);
|
|
|
|
else
|
|
|
|
Client_Destroy(c, NULL, Reason, false);
|
|
|
|
|
|
|
|
/* Are we still connected or were we killed, too? */
|
|
|
|
if (my_conn > NONE && Conn_GetClient(my_conn))
|
|
|
|
return CONNECTED;
|
|
|
|
else
|
|
|
|
return DISCONNECTED;
|
|
|
|
}
|
|
|
|
|
2013-01-01 14:57:03 +01:00
|
|
|
/**
|
|
|
|
* Send help for a given topic to the client.
|
|
|
|
*
|
|
|
|
* @param Client The client requesting help.
|
|
|
|
* @param Topoc The help topic requested.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
Help(CLIENT *Client, const char *Topic)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
size_t helptext_len, len_str, idx_start, lines = 0;
|
|
|
|
bool in_article = false;
|
|
|
|
|
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Topic != NULL);
|
|
|
|
|
|
|
|
helptext_len = array_bytes(&Conf_Helptext);
|
|
|
|
line = array_start(&Conf_Helptext);
|
|
|
|
while (helptext_len > 0) {
|
|
|
|
len_str = strlen(line) + 1;
|
|
|
|
assert(helptext_len >= len_str);
|
|
|
|
helptext_len -= len_str;
|
|
|
|
|
|
|
|
if (in_article) {
|
|
|
|
/* The first character in each article text line must
|
|
|
|
* be a TAB (ASCII 9) character which will be stripped
|
|
|
|
* in the output. If it is not a TAB, the end of the
|
|
|
|
* article has been reached. */
|
|
|
|
if (line[0] != '\t') {
|
|
|
|
if (lines > 0)
|
|
|
|
return CONNECTED;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A single '.' character indicates an empty line */
|
|
|
|
if (line[1] == '.' && line[2] == '\0')
|
|
|
|
idx_start = 2;
|
|
|
|
else
|
|
|
|
idx_start = 1;
|
|
|
|
|
|
|
|
if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
|
|
|
|
Client_ID(Client),
|
|
|
|
&line[idx_start]))
|
|
|
|
return DISCONNECTED;
|
|
|
|
lines++;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (line[0] == '-' && line[1] == ' '
|
|
|
|
&& strcasecmp(&line[2], Topic) == 0)
|
|
|
|
in_article = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
line += len_str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Help topic not found (or empty)! */
|
|
|
|
if (!IRC_WriteStrClient(Client, "NOTICE %s :No help for \"%s\" found!",
|
|
|
|
Client_ID(Client), Topic))
|
|
|
|
return DISCONNECTED;
|
|
|
|
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2013-08-31 14:15:09 +02:00
|
|
|
/**
|
|
|
|
* Get pointer to a static string representing the connection "options".
|
|
|
|
*
|
|
|
|
* @param Idx Connection index.
|
|
|
|
* @return Pointer to static (global) string buffer.
|
|
|
|
*/
|
2005-07-31 22:13:07 +02:00
|
|
|
static char *
|
2012-08-06 01:35:56 +02:00
|
|
|
#ifdef ZLIB
|
|
|
|
Option_String(CONN_ID Idx)
|
|
|
|
#else
|
|
|
|
Option_String(UNUSED CONN_ID Idx)
|
|
|
|
#endif
|
2003-03-19 22:16:53 +01:00
|
|
|
{
|
2005-03-19 19:43:48 +01:00
|
|
|
static char option_txt[8];
|
2006-05-10 23:24:01 +02:00
|
|
|
UINT16 options;
|
2003-03-19 22:16:53 +01:00
|
|
|
|
2013-08-31 14:15:09 +02:00
|
|
|
assert(Idx != NONE);
|
|
|
|
|
|
|
|
options = Conn_Options(Idx);
|
2006-05-10 23:24:01 +02:00
|
|
|
strcpy(option_txt, "F"); /* No idea what this means, but the
|
|
|
|
* original ircd sends it ... */
|
2013-08-31 14:15:09 +02:00
|
|
|
#ifdef SSL_SUPPORT
|
|
|
|
if(options & CONN_SSL) /* SSL encrypted link */
|
|
|
|
strlcat(option_txt, "s", sizeof(option_txt));
|
|
|
|
#endif
|
2003-12-26 16:55:07 +01:00
|
|
|
#ifdef ZLIB
|
2013-08-31 14:15:09 +02:00
|
|
|
if(options & CONN_ZIP) /* zlib compression enabled */
|
|
|
|
strlcat(option_txt, "z", sizeof(option_txt));
|
2003-03-19 22:16:53 +01:00
|
|
|
#endif
|
2013-08-31 14:15:09 +02:00
|
|
|
LogDebug(" *** %d: %d = %s", Idx, options, option_txt);
|
2003-03-19 22:16:53 +01:00
|
|
|
|
|
|
|
return option_txt;
|
|
|
|
} /* Option_String */
|
|
|
|
|
2016-01-04 20:45:49 +01:00
|
|
|
/**
|
|
|
|
* Send a message to target(s).
|
|
|
|
*
|
|
|
|
* This function is used by IRC_{PRIVMSG|NOTICE|SQUERY} to actualy
|
|
|
|
* send the message(s).
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @param ForceType Required type of the destination of the message(s).
|
|
|
|
* @param SendErrors Whether to report errors back to the client or not.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2008-05-05 18:06:43 +02:00
|
|
|
static bool
|
2008-06-11 20:35:50 +02:00
|
|
|
Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
|
2008-05-05 18:06:43 +02:00
|
|
|
{
|
|
|
|
CLIENT *cl, *from;
|
2012-01-16 11:43:22 +01:00
|
|
|
CL2CHAN *cl2chan;
|
2008-05-05 18:06:43 +02:00
|
|
|
CHANNEL *chan;
|
2008-06-11 20:35:50 +02:00
|
|
|
char *currentTarget = Req->argv[0];
|
2016-01-04 22:11:47 +01:00
|
|
|
char *strtok_last = NULL;
|
2012-09-17 00:36:10 +02:00
|
|
|
char *message = NULL;
|
2016-01-04 22:11:47 +01:00
|
|
|
char *targets[MAX_HNDL_TARGETS];
|
|
|
|
int i, target_nr = 0;
|
2008-05-05 18:06:43 +02:00
|
|
|
|
2008-06-13 06:54:05 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
2008-05-05 18:06:43 +02:00
|
|
|
|
2008-06-11 20:35:50 +02:00
|
|
|
if (Req->argc == 0) {
|
|
|
|
if (!SendErrors)
|
2008-08-16 14:12:03 +02:00
|
|
|
return CONNECTED;
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(Client, ERR_NORECIPIENT_MSG,
|
2008-05-05 18:06:43 +02:00
|
|
|
Client_ID(Client), Req->command);
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
|
|
|
if (Req->argc == 1) {
|
|
|
|
if (!SendErrors)
|
2008-08-16 14:12:03 +02:00
|
|
|
return CONNECTED;
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(Client, ERR_NOTEXTTOSEND_MSG,
|
2008-05-05 18:06:43 +02:00
|
|
|
Client_ID(Client));
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
|
|
|
if (Req->argc > 2) {
|
|
|
|
if (!SendErrors)
|
2008-08-16 14:12:03 +02:00
|
|
|
return CONNECTED;
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
2008-05-05 18:06:43 +02:00
|
|
|
Client_ID(Client), Req->command);
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
2008-05-05 18:06:43 +02:00
|
|
|
|
2015-09-03 16:22:36 +02:00
|
|
|
if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
|
2008-05-05 18:06:43 +02:00
|
|
|
from = Client_Search(Req->prefix);
|
|
|
|
else
|
|
|
|
from = Client;
|
2008-07-27 15:50:51 +02:00
|
|
|
if (!from)
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
|
2008-05-05 18:06:43 +02:00
|
|
|
Client_ID(Client), Req->prefix);
|
|
|
|
|
2012-09-17 00:36:10 +02:00
|
|
|
#ifdef ICONV
|
|
|
|
if (Client_Conn(Client) > NONE)
|
|
|
|
message = Conn_EncodingFrom(Client_Conn(Client), Req->argv[1]);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
message = Req->argv[1];
|
|
|
|
|
2008-06-13 06:54:05 +02:00
|
|
|
/* handle msgtarget = msgto *("," msgto) */
|
2016-01-04 22:11:47 +01:00
|
|
|
currentTarget = strtok_r(currentTarget, ",", &strtok_last);
|
2008-08-17 14:59:36 +02:00
|
|
|
ngt_UpperStr(Req->command);
|
2008-05-05 18:06:43 +02:00
|
|
|
|
Send_Message: Fix handling of "empty" targets
Clients can specify multiple targets for the "PRIVMSG", "NOTICE", and
"SQUERY" commands, separated by commas (e. g. "PRIVMSG a,#b,c :text").
Since commit 49ab79d0 ("Limit the number of message targes, and suppress
duplicates"), ngIRCd crashed when the client sent the separator character
only as target(s), e. g. "," or ",,,," etc.!
This patch fixes the bug and adds a test case for this issue.
Thanks to Florian Westphal <fw@strlen.de> for spotting the issue!
2016-01-07 01:54:11 +01:00
|
|
|
/* Please note that "currentTarget" is NULL when the target contains
|
|
|
|
* the separator character only, e. g. "," or ",,,," etc.! */
|
|
|
|
while (currentTarget) {
|
2016-01-04 22:11:47 +01:00
|
|
|
/* Make sure that there hasn't been such a target already: */
|
|
|
|
targets[target_nr++] = currentTarget;
|
|
|
|
for(i = 0; i < target_nr - 1; i++) {
|
|
|
|
if (strcasecmp(currentTarget, targets[i]) == 0)
|
|
|
|
goto send_next_target;
|
|
|
|
}
|
|
|
|
|
2008-06-13 06:54:05 +02:00
|
|
|
/* Check for and handle valid <msgto> of form:
|
|
|
|
* RFC 2812 2.3.1:
|
|
|
|
* msgto = channel / ( user [ "%" host ] "@" servername )
|
|
|
|
* msgto =/ ( user "%" host ) / targetmask
|
|
|
|
* msgto =/ nickname / ( nickname "!" user "@" host )
|
|
|
|
*/
|
2008-06-11 20:35:50 +02:00
|
|
|
if (strchr(currentTarget, '!') == NULL)
|
2008-06-13 06:54:05 +02:00
|
|
|
/* nickname */
|
2008-06-11 20:35:50 +02:00
|
|
|
cl = Client_Search(currentTarget);
|
|
|
|
else
|
|
|
|
cl = NULL;
|
2008-06-13 06:54:05 +02:00
|
|
|
|
2008-06-11 20:35:50 +02:00
|
|
|
if (cl == NULL) {
|
2008-06-13 06:54:05 +02:00
|
|
|
/* If currentTarget isn't a nickname check for:
|
|
|
|
* user ["%" host] "@" servername
|
|
|
|
* user "%" host
|
|
|
|
* nickname "!" user "@" host
|
|
|
|
*/
|
|
|
|
char target[COMMAND_LEN];
|
2008-06-11 20:35:50 +02:00
|
|
|
char * nick = NULL;
|
|
|
|
char * user = NULL;
|
|
|
|
char * host = NULL;
|
|
|
|
char * server = NULL;
|
|
|
|
|
2008-06-13 06:54:05 +02:00
|
|
|
strlcpy(target, currentTarget, COMMAND_LEN);
|
2008-06-11 20:35:50 +02:00
|
|
|
server = strchr(target, '@');
|
|
|
|
if (server) {
|
|
|
|
*server = '\0';
|
|
|
|
server++;
|
|
|
|
}
|
|
|
|
host = strchr(target, '%');
|
|
|
|
if (host) {
|
|
|
|
*host = '\0';
|
|
|
|
host++;
|
|
|
|
}
|
|
|
|
user = strchr(target, '!');
|
|
|
|
if (user) {
|
2008-06-13 06:54:05 +02:00
|
|
|
/* msgto form: nick!user@host */
|
2008-06-11 20:35:50 +02:00
|
|
|
*user = '\0';
|
|
|
|
user++;
|
|
|
|
nick = target;
|
2008-06-13 06:54:05 +02:00
|
|
|
host = server; /* not "@server" but "@host" */
|
2008-06-11 20:35:50 +02:00
|
|
|
} else {
|
|
|
|
user = target;
|
|
|
|
}
|
|
|
|
|
2008-06-13 06:54:05 +02:00
|
|
|
for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
|
2008-08-17 17:29:41 +02:00
|
|
|
if (Client_Type(cl) != CLIENT_USER &&
|
|
|
|
Client_Type(cl) != CLIENT_SERVICE)
|
2008-06-13 06:54:05 +02:00
|
|
|
continue;
|
2008-08-17 17:13:39 +02:00
|
|
|
if (nick != NULL && host != NULL) {
|
2012-01-21 13:21:36 +01:00
|
|
|
if (strcasecmp(nick, Client_ID(cl)) == 0 &&
|
|
|
|
strcasecmp(user, Client_User(cl)) == 0 &&
|
2012-11-24 13:37:56 +01:00
|
|
|
strcasecmp(host, Client_HostnameDisplayed(cl)) == 0)
|
2008-06-13 06:54:05 +02:00
|
|
|
break;
|
|
|
|
else
|
2008-06-11 20:35:50 +02:00
|
|
|
continue;
|
|
|
|
}
|
2008-06-13 06:54:05 +02:00
|
|
|
if (strcasecmp(user, Client_User(cl)) != 0)
|
|
|
|
continue;
|
2008-07-27 15:50:51 +02:00
|
|
|
if (host != NULL && strcasecmp(host,
|
2012-11-24 13:37:56 +01:00
|
|
|
Client_HostnameDisplayed(cl)) != 0)
|
2008-06-13 06:54:05 +02:00
|
|
|
continue;
|
|
|
|
if (server != NULL && strcasecmp(server,
|
2008-07-27 15:50:51 +02:00
|
|
|
Client_ID(Client_Introducer(cl))) != 0)
|
2008-06-13 06:54:05 +02:00
|
|
|
continue;
|
|
|
|
break;
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cl) {
|
|
|
|
/* Target is a user, enforce type */
|
2008-08-16 17:52:02 +02:00
|
|
|
#ifndef STRICT_RFC
|
|
|
|
if (Client_Type(cl) != ForceType &&
|
|
|
|
!(ForceType == CLIENT_USER &&
|
|
|
|
(Client_Type(cl) == CLIENT_USER ||
|
|
|
|
Client_Type(cl) == CLIENT_SERVICE))) {
|
|
|
|
#else
|
2008-06-11 20:35:50 +02:00
|
|
|
if (Client_Type(cl) != ForceType) {
|
2008-08-16 17:52:02 +02:00
|
|
|
#endif
|
2013-08-06 23:37:21 +02:00
|
|
|
if (SendErrors && !IRC_WriteErrClient(
|
2012-01-16 12:37:37 +01:00
|
|
|
from, ERR_NOSUCHNICK_MSG,Client_ID(from),
|
|
|
|
currentTarget))
|
|
|
|
return DISCONNECTED;
|
|
|
|
goto send_next_target;
|
2008-08-16 14:12:03 +02:00
|
|
|
}
|
2008-08-17 15:19:45 +02:00
|
|
|
|
|
|
|
#ifndef STRICT_RFC
|
|
|
|
if (ForceType == CLIENT_SERVICE &&
|
|
|
|
(Conn_Options(Client_Conn(Client_NextHop(cl)))
|
|
|
|
& CONN_RFC1459)) {
|
|
|
|
/* SQUERY command but RFC 1459 link: convert
|
|
|
|
* request to PRIVMSG command */
|
|
|
|
Req->command = "PRIVMSG";
|
|
|
|
}
|
|
|
|
#endif
|
2012-10-08 12:11:04 +02:00
|
|
|
if (Client_HasMode(cl, 'b') &&
|
|
|
|
!Client_HasMode(from, 'R') &&
|
|
|
|
!Client_HasMode(from, 'o') &&
|
|
|
|
!(Client_Type(from) == CLIENT_SERVER) &&
|
|
|
|
!(Client_Type(from) == CLIENT_SERVICE)) {
|
2013-08-06 23:37:21 +02:00
|
|
|
if (SendErrors && !IRC_WriteErrClient(from,
|
2012-10-19 18:37:33 +02:00
|
|
|
ERR_NONONREG_MSG,
|
2012-10-08 12:11:04 +02:00
|
|
|
Client_ID(from), Client_ID(cl)))
|
|
|
|
return DISCONNECTED;
|
|
|
|
goto send_next_target;
|
|
|
|
}
|
2008-08-17 15:19:45 +02:00
|
|
|
|
2012-01-16 11:43:22 +01:00
|
|
|
if (Client_HasMode(cl, 'C')) {
|
|
|
|
cl2chan = Channel_FirstChannelOf(cl);
|
|
|
|
while (cl2chan) {
|
|
|
|
chan = Channel_GetChannel(cl2chan);
|
|
|
|
if (Channel_IsMemberOf(chan, from))
|
|
|
|
break;
|
|
|
|
cl2chan = Channel_NextChannelOf(cl, cl2chan);
|
|
|
|
}
|
|
|
|
if (!cl2chan) {
|
2013-08-06 23:37:21 +02:00
|
|
|
if (SendErrors && !IRC_WriteErrClient(
|
2012-01-16 11:43:22 +01:00
|
|
|
from, ERR_NOTONSAMECHANNEL_MSG,
|
|
|
|
Client_ID(from), Client_ID(cl)))
|
|
|
|
return DISCONNECTED;
|
|
|
|
goto send_next_target;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-16 14:12:03 +02:00
|
|
|
if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
|
2013-08-04 19:28:04 +02:00
|
|
|
&& Client_HasMode(cl, 'a')) {
|
2008-06-11 20:35:50 +02:00
|
|
|
/* Target is away */
|
2008-08-16 14:12:03 +02:00
|
|
|
if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
|
|
|
|
Client_ID(from),
|
|
|
|
Client_ID(cl),
|
|
|
|
Client_Away(cl)))
|
2008-06-11 20:35:50 +02:00
|
|
|
return DISCONNECTED;
|
2008-06-13 06:54:05 +02:00
|
|
|
}
|
|
|
|
if (Client_Conn(from) > NONE) {
|
2008-06-11 20:35:50 +02:00
|
|
|
Conn_UpdateIdle(Client_Conn(from));
|
|
|
|
}
|
2008-08-16 18:26:19 +02:00
|
|
|
if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
|
|
|
|
Req->command, Client_ID(cl),
|
2012-09-17 00:36:10 +02:00
|
|
|
message))
|
2008-08-16 14:12:03 +02:00
|
|
|
return DISCONNECTED;
|
2009-01-18 00:12:28 +01:00
|
|
|
} else if (ForceType != CLIENT_SERVICE
|
|
|
|
&& (chan = Channel_Search(currentTarget))) {
|
2016-01-04 20:45:49 +01:00
|
|
|
/* Target is a channel */
|
2009-01-18 00:12:28 +01:00
|
|
|
if (!Channel_Write(chan, from, Client, Req->command,
|
2012-09-17 00:36:10 +02:00
|
|
|
SendErrors, message))
|
2009-01-18 00:12:28 +01:00
|
|
|
return DISCONNECTED;
|
2008-08-17 15:06:33 +02:00
|
|
|
} else if (ForceType != CLIENT_SERVICE
|
|
|
|
&& strchr("$#", currentTarget[0])
|
2008-07-27 15:50:51 +02:00
|
|
|
&& strchr(currentTarget, '.')) {
|
2016-01-04 20:45:49 +01:00
|
|
|
/* $#: server/host mask, RFC 2812, sec. 3.3.1 */
|
2008-08-16 18:26:19 +02:00
|
|
|
if (!Send_Message_Mask(from, Req->command, currentTarget,
|
2012-09-17 00:36:10 +02:00
|
|
|
message, SendErrors))
|
2008-08-16 14:12:03 +02:00
|
|
|
return DISCONNECTED;
|
2008-06-11 20:35:50 +02:00
|
|
|
} else {
|
|
|
|
if (!SendErrors)
|
2008-08-16 14:12:03 +02:00
|
|
|
return CONNECTED;
|
2013-08-06 23:37:21 +02:00
|
|
|
if (!IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
|
2008-06-11 20:35:50 +02:00
|
|
|
Client_ID(from), currentTarget))
|
2008-08-16 14:12:03 +02:00
|
|
|
return DISCONNECTED;
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
|
|
|
|
2012-01-16 11:43:22 +01:00
|
|
|
send_next_target:
|
2016-01-04 22:11:47 +01:00
|
|
|
currentTarget = strtok_r(NULL, ",", &strtok_last);
|
|
|
|
if (!currentTarget)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Conn_SetPenalty(Client_Conn(Client), 1);
|
|
|
|
|
|
|
|
if (target_nr >= MAX_HNDL_TARGETS) {
|
|
|
|
/* Too many targets given! */
|
|
|
|
return IRC_WriteErrClient(Client,
|
|
|
|
ERR_TOOMANYTARGETS_MSG,
|
|
|
|
currentTarget);
|
|
|
|
}
|
2008-05-05 18:06:43 +02:00
|
|
|
}
|
|
|
|
|
2008-06-11 20:35:50 +02:00
|
|
|
return CONNECTED;
|
2008-05-05 18:06:43 +02:00
|
|
|
} /* Send_Message */
|
|
|
|
|
2016-01-04 20:45:49 +01:00
|
|
|
/**
|
|
|
|
* Send a message to "target mask" target(s).
|
|
|
|
*
|
|
|
|
* See RFC 2812, sec. 3.3.1 for details.
|
|
|
|
*
|
|
|
|
* @param from The client from which this command has been received.
|
|
|
|
* @param command The command to use (PRIVMSG, NOTICE, ...).
|
|
|
|
* @param targetMask The "target mask" (will be verified by this function).
|
|
|
|
* @param message The message to send.
|
|
|
|
* @param SendErrors Whether to report errors back to the client or not.
|
|
|
|
* @return CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2008-06-11 20:35:50 +02:00
|
|
|
static bool
|
2008-08-16 18:26:19 +02:00
|
|
|
Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
|
|
|
|
char * message, bool SendErrors)
|
2008-06-11 20:35:50 +02:00
|
|
|
{
|
|
|
|
CLIENT *cl;
|
|
|
|
bool client_match;
|
|
|
|
char *mask = targetMask + 1;
|
2009-01-18 00:33:34 +01:00
|
|
|
const char *check_wildcards;
|
2008-06-11 20:35:50 +02:00
|
|
|
|
|
|
|
cl = NULL;
|
|
|
|
|
2013-08-04 19:28:04 +02:00
|
|
|
if (!Client_HasMode(from, 'o')) {
|
2008-06-11 20:35:50 +02:00
|
|
|
if (!SendErrors)
|
|
|
|
return true;
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
|
2008-07-27 15:50:51 +02:00
|
|
|
Client_ID(from));
|
2008-06-11 20:35:50 +02:00
|
|
|
}
|
|
|
|
|
2009-01-18 00:33:34 +01:00
|
|
|
/*
|
|
|
|
* RFC 2812, sec. 3.3.1 requires that targetMask have at least one
|
|
|
|
* dot (".") and no wildcards ("*", "?") following the last one.
|
|
|
|
*/
|
2009-01-20 16:49:34 +01:00
|
|
|
check_wildcards = strrchr(targetMask, '.');
|
2016-01-04 20:37:13 +01:00
|
|
|
if (!check_wildcards || check_wildcards[strcspn(check_wildcards, "*?")]) {
|
2009-01-18 00:33:34 +01:00
|
|
|
if (!SendErrors)
|
|
|
|
return true;
|
2016-01-04 22:10:38 +01:00
|
|
|
return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL_MSG,
|
|
|
|
targetMask);
|
2009-01-18 00:33:34 +01:00
|
|
|
}
|
|
|
|
|
2008-06-11 20:35:50 +02:00
|
|
|
if (targetMask[0] == '#') {
|
2016-01-04 20:45:49 +01:00
|
|
|
/* #: hostmask, see RFC 2812, sec. 3.3.1 */
|
2008-06-11 20:35:50 +02:00
|
|
|
for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
|
|
|
|
if (Client_Type(cl) != CLIENT_USER)
|
|
|
|
continue;
|
|
|
|
client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
|
|
|
|
if (client_match)
|
2008-08-16 18:26:19 +02:00
|
|
|
if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
|
|
|
|
command, Client_ID(cl), message))
|
2008-06-11 20:35:50 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
2016-01-04 20:45:49 +01:00
|
|
|
/* $: server mask, see RFC 2812, sec. 3.3.1 */
|
|
|
|
assert(targetMask[0] == '$');
|
2008-06-11 20:35:50 +02:00
|
|
|
for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
|
|
|
|
if (Client_Type(cl) != CLIENT_USER)
|
|
|
|
continue;
|
2008-06-13 06:54:05 +02:00
|
|
|
client_match = MatchCaseInsensitive(mask,
|
2008-07-27 15:50:51 +02:00
|
|
|
Client_ID(Client_Introducer(cl)));
|
2008-06-11 20:35:50 +02:00
|
|
|
if (client_match)
|
2008-08-16 18:26:19 +02:00
|
|
|
if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
|
|
|
|
command, Client_ID(cl), message))
|
2008-06-11 20:35:50 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return CONNECTED;
|
|
|
|
} /* Send_Message_Mask */
|
|
|
|
|
2001-12-14 09:13:43 +01:00
|
|
|
/* -eof- */
|