2002-02-28 00:26:21 +01:00
|
|
|
/*
|
|
|
|
* ngIRCd -- The Next Generation IRC Daemon
|
2010-02-11 00:01:53 +01:00
|
|
|
* Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
|
2002-02-28 00:26:21 +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:21 +01:00
|
|
|
*/
|
|
|
|
|
2002-03-12 15:37:51 +01:00
|
|
|
#include "portab.h"
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2010-12-27 17:14:14 +01:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* Login and logout
|
|
|
|
*/
|
|
|
|
|
2002-03-12 15:37:51 +01:00
|
|
|
#include "imp.h"
|
2002-02-28 00:26:21 +01:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2004-01-17 04:17:49 +01:00
|
|
|
#include <strings.h>
|
2010-07-12 13:22:19 +02:00
|
|
|
#include <signal.h>
|
2010-07-12 12:56:33 +02:00
|
|
|
#include <unistd.h>
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
#include "ngircd.h"
|
2002-12-30 18:15:42 +01:00
|
|
|
#include "conn-func.h"
|
2002-12-30 01:01:42 +01:00
|
|
|
#include "conf.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "channel.h"
|
2010-07-12 12:56:33 +02:00
|
|
|
#include "io.h"
|
2002-02-28 00:26:21 +01:00
|
|
|
#include "log.h"
|
|
|
|
#include "messages.h"
|
2010-07-12 12:56:33 +02:00
|
|
|
#include "pam.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "parse.h"
|
2003-01-08 23:28:12 +01:00
|
|
|
#include "irc.h"
|
2002-11-30 18:39:56 +01:00
|
|
|
#include "irc-info.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "irc-write.h"
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2002-03-12 15:37:51 +01:00
|
|
|
#include "exp.h"
|
2002-02-28 00:26:21 +01:00
|
|
|
#include "irc-login.h"
|
|
|
|
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool Hello_User PARAMS(( CLIENT *Client ));
|
2010-07-12 12:56:33 +02:00
|
|
|
static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
|
2005-07-31 22:13:07 +02:00
|
|
|
static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
|
2008-08-17 17:27:45 +02:00
|
|
|
static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
|
2010-07-12 12:56:33 +02:00
|
|
|
static void Reject_Client PARAMS((CLIENT *Client));
|
|
|
|
|
2008-08-13 21:55:22 +02:00
|
|
|
static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
|
|
|
|
void *i));
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2010-07-12 12:56:33 +02:00
|
|
|
#ifdef PAM
|
|
|
|
static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
|
|
|
|
#endif
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
/**
|
2010-12-29 14:09:46 +01:00
|
|
|
* Handler for the IRC "PASS" command.
|
|
|
|
*
|
2006-10-01 21:05:00 +02:00
|
|
|
* See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
|
2010-12-29 14:09:46 +01:00
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
2006-10-01 21:05:00 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2002-05-27 15:09:26 +02:00
|
|
|
IRC_PASS( CLIENT *Client, REQUEST *Req )
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2006-10-01 21:05:00 +02:00
|
|
|
char *type, *orig_flags;
|
|
|
|
int protohigh, protolow;
|
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
assert( Client != NULL );
|
|
|
|
assert( Req != NULL );
|
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
/* Return an error if this is not a local client */
|
|
|
|
if (Client_Conn(Client) <= NONE)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
|
|
|
|
/* Not yet registered "unknown" connection, PASS with one
|
|
|
|
* argument: either a regular client, service, or server
|
|
|
|
* using the old RFC 1459 section 4.1.1 syntax. */
|
2008-08-14 23:23:04 +02:00
|
|
|
LogDebug("Connection %d: got PASS command (RFC 1459) ...",
|
2006-10-01 21:05:00 +02:00
|
|
|
Client_Conn(Client));
|
|
|
|
} else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
|
|
|
|
Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
|
|
|
|
(Req->argc == 3 || Req->argc == 4)) {
|
|
|
|
/* Not yet registered "unknown" connection or outgoing server
|
|
|
|
* link, PASS with three or four argument: server using the
|
|
|
|
* RFC 2813 section 4.1.1 syntax. */
|
2008-08-14 23:23:04 +02:00
|
|
|
LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...",
|
2006-10-01 21:05:00 +02:00
|
|
|
Client_Conn(Client));
|
|
|
|
} else if (Client_Type(Client) == CLIENT_UNKNOWN ||
|
|
|
|
Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
|
|
|
|
/* Unregistered connection, but wrong number of arguments: */
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
} else {
|
|
|
|
/* Registered connection, PASS command is not allowed! */
|
|
|
|
return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
|
|
|
|
Client_ID(Client));
|
|
|
|
}
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
Client_SetPassword(Client, Req->argv[0]);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
/* Protocol version */
|
|
|
|
if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
|
|
|
|
int c2, c4;
|
|
|
|
|
|
|
|
c2 = Req->argv[1][2];
|
|
|
|
c4 = Req->argv[1][4];
|
2002-09-03 22:39:54 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
Req->argv[1][4] = '\0';
|
|
|
|
protolow = atoi(&Req->argv[1][2]);
|
|
|
|
Req->argv[1][2] = '\0';
|
|
|
|
protohigh = atoi(Req->argv[1]);
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
Req->argv[1][2] = c2;
|
|
|
|
Req->argv[1][4] = c4;
|
2008-08-14 23:23:04 +02:00
|
|
|
|
|
|
|
Client_SetType(Client, CLIENT_GOTPASS_2813);
|
|
|
|
} else {
|
2006-10-01 21:05:00 +02:00
|
|
|
protohigh = protolow = 0;
|
2008-08-14 23:23:04 +02:00
|
|
|
Client_SetType(Client, CLIENT_GOTPASS);
|
|
|
|
}
|
2006-10-01 21:05:00 +02:00
|
|
|
|
|
|
|
/* Protocol type, see doc/Protocol.txt */
|
|
|
|
if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
|
|
|
|
type = &Req->argv[1][4];
|
|
|
|
else
|
|
|
|
type = NULL;
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
/* Protocol flags/options */
|
|
|
|
if (Req->argc >= 4)
|
|
|
|
orig_flags = Req->argv[3];
|
|
|
|
else
|
|
|
|
orig_flags = "";
|
2002-09-02 21:04:30 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
/* Implementation, version and IRC+ flags */
|
|
|
|
if (Req->argc >= 3) {
|
|
|
|
char *impl, *ptr, *serverver, *flags;
|
2002-11-27 00:07:24 +01:00
|
|
|
|
2002-09-02 21:04:30 +02:00
|
|
|
impl = Req->argv[2];
|
2006-10-01 21:05:00 +02:00
|
|
|
ptr = strchr(impl, '|');
|
|
|
|
if (ptr)
|
|
|
|
*ptr = '\0';
|
2002-09-02 21:04:30 +02:00
|
|
|
|
2006-10-01 21:05:00 +02:00
|
|
|
if (type && strcmp(type, PROTOIRCPLUS) == 0) {
|
|
|
|
/* The peer seems to be a server which supports the
|
|
|
|
* IRC+ protocol (see doc/Protocol.txt). */
|
2011-01-23 18:38:36 +01:00
|
|
|
serverver = ptr ? ptr + 1 : "?";
|
|
|
|
flags = strchr(ptr ? serverver : impl, ':');
|
2006-10-01 21:05:00 +02:00
|
|
|
if (flags) {
|
2002-09-02 21:04:30 +02:00
|
|
|
*flags = '\0';
|
|
|
|
flags++;
|
2006-10-01 21:05:00 +02:00
|
|
|
} else
|
|
|
|
flags = "";
|
|
|
|
Log(LOG_INFO,
|
2011-01-16 23:24:41 +01:00
|
|
|
"Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
|
|
|
|
Client_Conn(Client), impl, serverver,
|
|
|
|
protohigh, protolow, flags);
|
2006-10-01 21:05:00 +02:00
|
|
|
} else {
|
|
|
|
/* The peer seems to be a server supporting the
|
|
|
|
* "original" IRC protocol (RFC 2813). */
|
|
|
|
if (strchr(orig_flags, 'Z'))
|
|
|
|
flags = "Z";
|
|
|
|
else
|
|
|
|
flags = "";
|
|
|
|
Log(LOG_INFO,
|
2011-01-16 23:24:41 +01:00
|
|
|
"Peer on connection %d announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
|
|
|
|
Client_Conn(Client), impl,
|
|
|
|
protohigh, protolow, flags);
|
2002-09-02 21:04:30 +02:00
|
|
|
}
|
2006-10-01 21:05:00 +02:00
|
|
|
Client_SetFlags(Client, flags);
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
2006-10-01 21:05:00 +02:00
|
|
|
|
|
|
|
return CONNECTED;
|
2002-02-28 00:26:21 +01:00
|
|
|
} /* IRC_PASS */
|
|
|
|
|
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/**
|
2010-12-29 14:09:46 +01:00
|
|
|
* Handler for the IRC "NICK" command.
|
|
|
|
*
|
|
|
|
* See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
|
|
|
|
*
|
2005-05-18 01:24:43 +02:00
|
|
|
* This function implements the IRC command "NICK" which is used to register
|
|
|
|
* with the server, to change already registered nicknames and to introduce
|
|
|
|
* new users which are connected to other servers.
|
2010-12-29 14:09:46 +01:00
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
2005-05-18 01:24:43 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2002-05-27 15:09:26 +02:00
|
|
|
IRC_NICK( CLIENT *Client, REQUEST *Req )
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
|
|
|
CLIENT *intr_c, *target, *c;
|
2008-08-13 02:00:54 +02:00
|
|
|
char *nick, *user, *hostname, *modes, *info;
|
|
|
|
int token, hops;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
assert( Client != NULL );
|
|
|
|
assert( Req != NULL );
|
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Some IRC clients, for example BitchX, send the NICK and USER
|
|
|
|
* commands in the wrong order ... */
|
2008-08-16 17:33:53 +02:00
|
|
|
if(Client_Type(Client) == CLIENT_UNKNOWN
|
|
|
|
|| Client_Type(Client) == CLIENT_GOTPASS
|
|
|
|
|| Client_Type(Client) == CLIENT_GOTNICK
|
|
|
|
#ifndef STRICT_RFC
|
|
|
|
|| Client_Type(Client) == CLIENT_GOTUSER
|
2002-02-28 00:26:21 +01:00
|
|
|
#endif
|
2008-08-16 17:33:53 +02:00
|
|
|
|| Client_Type(Client) == CLIENT_USER
|
|
|
|
|| Client_Type(Client) == CLIENT_SERVICE
|
|
|
|
|| (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2005-05-18 01:24:43 +02:00
|
|
|
/* User registration or change of nickname */
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Wrong number of arguments? */
|
|
|
|
if( Req->argc != 1 )
|
|
|
|
return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID( Client ),
|
|
|
|
Req->command );
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Search "target" client */
|
2002-02-28 00:26:21 +01:00
|
|
|
if( Client_Type( Client ) == CLIENT_SERVER )
|
|
|
|
{
|
2002-03-25 18:08:54 +01:00
|
|
|
target = Client_Search( Req->prefix );
|
2005-05-18 01:24:43 +02:00
|
|
|
if( ! target )
|
|
|
|
return IRC_WriteStrClient( Client,
|
|
|
|
ERR_NOSUCHNICK_MSG,
|
|
|
|
Client_ID( Client ),
|
|
|
|
Req->argv[0] );
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Is this a restricted client? */
|
|
|
|
if( Client_HasMode( Client, 'r' ))
|
|
|
|
return IRC_WriteStrClient( Client,
|
|
|
|
ERR_RESTRICTED_MSG,
|
|
|
|
Client_ID( Client ));
|
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
target = Client;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef STRICT_RFC
|
2005-05-18 01:24:43 +02:00
|
|
|
/* If the clients tries to change to its own nickname we won't
|
|
|
|
* do anything. This is how the original ircd behaves and some
|
|
|
|
* clients (for example Snak) expect it to be like this.
|
|
|
|
* But I doubt that this is "really the right thing" ... */
|
|
|
|
if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
|
|
|
|
return CONNECTED;
|
2002-02-28 00:26:21 +01:00
|
|
|
#endif
|
2002-04-08 18:37:50 +02:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Check that the new nickname is available. Special case:
|
|
|
|
* the client only changes from/to upper to lower case. */
|
2002-02-28 00:26:21 +01:00
|
|
|
if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
|
|
|
|
{
|
2005-05-18 01:24:43 +02:00
|
|
|
if( ! Client_CheckNick( target, Req->argv[0] ))
|
|
|
|
return CONNECTED;
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
|
2008-08-16 17:33:53 +02:00
|
|
|
if (Client_Type(target) != CLIENT_USER &&
|
|
|
|
Client_Type(target) != CLIENT_SERVICE &&
|
|
|
|
Client_Type(target) != CLIENT_SERVER) {
|
2005-05-18 01:24:43 +02:00
|
|
|
/* New client */
|
2009-01-10 00:44:34 +01:00
|
|
|
LogDebug("Connection %d: got valid NICK command ...",
|
2005-05-18 01:24:43 +02:00
|
|
|
Client_Conn( Client ));
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Register new nickname of this client */
|
2002-02-28 00:26:21 +01:00
|
|
|
Client_SetID( target, Req->argv[0] );
|
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* If we received a valid USER command already then
|
|
|
|
* register the new client! */
|
|
|
|
if( Client_Type( Client ) == CLIENT_GOTUSER )
|
|
|
|
return Hello_User( Client );
|
|
|
|
else
|
|
|
|
Client_SetType( Client, CLIENT_GOTNICK );
|
2008-08-16 17:33:53 +02:00
|
|
|
} else {
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Nickname change */
|
2008-05-24 21:50:54 +02:00
|
|
|
if (Client_Conn(target) > NONE) {
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Local client */
|
2008-05-24 21:50:54 +02:00
|
|
|
Log(LOG_INFO,
|
2008-08-16 17:33:53 +02:00
|
|
|
"%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
|
|
|
|
Client_TypeText(target), Client_Mask(target),
|
|
|
|
Client_Conn(target), Client_ID(target),
|
|
|
|
Req->argv[0]);
|
2008-05-24 21:50:54 +02:00
|
|
|
Conn_UpdateIdle(Client_Conn(target));
|
2008-08-16 17:33:53 +02:00
|
|
|
} else {
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Remote client */
|
2008-08-16 17:33:53 +02:00
|
|
|
LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
|
|
|
|
Client_TypeText(target),
|
|
|
|
Client_Mask(target), Client_ID(target),
|
|
|
|
Req->argv[0]);
|
2002-04-08 18:37:50 +02:00
|
|
|
}
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Inform all users and servers (which have to know)
|
|
|
|
* of this nickname change */
|
|
|
|
if( Client_Type( Client ) == CLIENT_USER )
|
|
|
|
IRC_WriteStrClientPrefix( Client, Client,
|
|
|
|
"NICK :%s",
|
|
|
|
Req->argv[0] );
|
|
|
|
IRC_WriteStrServersPrefix( Client, target,
|
|
|
|
"NICK :%s", Req->argv[0] );
|
|
|
|
IRC_WriteStrRelatedPrefix( target, target, false,
|
|
|
|
"NICK :%s", Req->argv[0] );
|
2006-08-12 13:56:24 +02:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Register old nickname for WHOWAS queries */
|
|
|
|
Client_RegisterWhowas( target );
|
2006-08-12 13:56:24 +02:00
|
|
|
|
2005-05-18 01:24:43 +02:00
|
|
|
/* Save new nickname */
|
2002-02-28 00:26:21 +01:00
|
|
|
Client_SetID( target, Req->argv[0] );
|
2006-08-12 13:56:24 +02:00
|
|
|
|
2003-11-06 00:24:48 +01:00
|
|
|
IRC_SetPenalty( target, 2 );
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return CONNECTED;
|
2008-08-13 02:00:54 +02:00
|
|
|
} else if(Client_Type(Client) == CLIENT_SERVER ||
|
|
|
|
Client_Type(Client) == CLIENT_SERVICE) {
|
|
|
|
/* Server or service introduces new client */
|
|
|
|
|
|
|
|
/* Bad number of parameters? */
|
|
|
|
if (Req->argc != 2 && Req->argc != 7)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
|
|
|
|
if (Req->argc >= 7) {
|
2008-08-13 16:52:35 +02:00
|
|
|
/* RFC 2813 compatible syntax */
|
2008-08-13 02:00:54 +02:00
|
|
|
nick = Req->argv[0];
|
|
|
|
hops = atoi(Req->argv[1]);
|
|
|
|
user = Req->argv[2];
|
|
|
|
hostname = Req->argv[3];
|
|
|
|
token = atoi(Req->argv[4]);
|
|
|
|
modes = Req->argv[5] + 1;
|
|
|
|
info = Req->argv[6];
|
|
|
|
} else {
|
2008-08-13 16:52:35 +02:00
|
|
|
/* RFC 1459 compatible syntax */
|
2008-08-13 02:00:54 +02:00
|
|
|
nick = Req->argv[0];
|
|
|
|
hops = 1;
|
|
|
|
user = Req->argv[0];
|
|
|
|
hostname = Client_ID(Client);
|
|
|
|
token = atoi(Req->argv[1]);
|
|
|
|
modes = "";
|
|
|
|
info = Req->argv[0];
|
|
|
|
}
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2008-08-13 02:00:54 +02:00
|
|
|
c = Client_Search(nick);
|
|
|
|
if(c) {
|
2009-04-21 08:40:10 +02:00
|
|
|
/*
|
|
|
|
* the new nick is already present on this server:
|
|
|
|
* the new and the old one have to be disconnected now.
|
|
|
|
*/
|
2002-02-28 00:26:21 +01:00
|
|
|
Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
|
|
|
|
Kill_Nick( Req->argv[0], "Nick collision" );
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* Find the Server this client is connected to */
|
2008-08-13 02:00:54 +02:00
|
|
|
intr_c = Client_GetFromToken(Client, token);
|
2002-02-28 00:26:21 +01:00
|
|
|
if( ! intr_c )
|
|
|
|
{
|
|
|
|
Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
|
|
|
|
Kill_Nick( Req->argv[0], "Unknown server" );
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2008-08-13 02:00:54 +02:00
|
|
|
c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
|
|
|
|
token, modes, info, true);
|
2002-02-28 00:26:21 +01:00
|
|
|
if( ! c )
|
|
|
|
{
|
2009-04-21 08:40:10 +02:00
|
|
|
/* out of memory, need to disconnect client to keep network state consistent */
|
2002-02-28 00:26:21 +01:00
|
|
|
Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
|
|
|
|
Kill_Nick( Req->argv[0], "Server error" );
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2008-08-13 21:55:22 +02:00
|
|
|
/* RFC 2813: client is now fully registered, inform all the
|
|
|
|
* other servers about the new user.
|
|
|
|
* RFC 1459: announce the new client only after receiving the
|
|
|
|
* USER command, first we need more information! */
|
2008-08-16 01:24:35 +02:00
|
|
|
if (Req->argc < 7) {
|
2010-05-14 19:43:08 +02:00
|
|
|
LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
|
2008-08-13 21:55:22 +02:00
|
|
|
Client_Mask(c));
|
|
|
|
Client_SetType(c, CLIENT_GOTNICK);
|
2008-08-16 01:24:35 +02:00
|
|
|
} else
|
2008-08-17 17:27:45 +02:00
|
|
|
Introduce_Client(Client, c, CLIENT_USER);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
|
|
|
|
} /* IRC_NICK */
|
|
|
|
|
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
/**
|
2010-12-29 14:09:46 +01:00
|
|
|
* Handler for the IRC "USER" command.
|
|
|
|
*
|
|
|
|
* See RFC 2812, 3.1.3 "User message".
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
2008-08-13 02:24:06 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2008-08-13 02:24:06 +02:00
|
|
|
IRC_USER(CLIENT * Client, REQUEST * Req)
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2008-08-13 02:24:06 +02:00
|
|
|
CLIENT *c;
|
2003-12-27 14:01:12 +01:00
|
|
|
#ifdef IDENTAUTH
|
2005-03-19 19:43:48 +01:00
|
|
|
char *ptr;
|
2003-12-27 14:01:12 +01:00
|
|
|
#endif
|
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
if (Client_Type(Client) == CLIENT_GOTNICK ||
|
2002-02-28 00:26:21 +01:00
|
|
|
#ifndef STRICT_RFC
|
2008-08-13 02:24:06 +02:00
|
|
|
Client_Type(Client) == CLIENT_UNKNOWN ||
|
2002-02-28 00:26:21 +01:00
|
|
|
#endif
|
2008-08-13 02:24:06 +02:00
|
|
|
Client_Type(Client) == CLIENT_GOTPASS)
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2008-08-13 02:24:06 +02:00
|
|
|
/* New connection */
|
|
|
|
if (Req->argc != 4)
|
|
|
|
return IRC_WriteStrClient(Client,
|
|
|
|
ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client),
|
|
|
|
Req->command);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2004-02-04 20:56:04 +01:00
|
|
|
/* User name */
|
2003-12-27 14:01:12 +01:00
|
|
|
#ifdef IDENTAUTH
|
2008-08-13 02:24:06 +02:00
|
|
|
ptr = Client_User(Client);
|
|
|
|
if (!ptr || !*ptr || *ptr == '~')
|
|
|
|
Client_SetUser(Client, Req->argv[0], false);
|
2004-02-04 20:56:04 +01:00
|
|
|
#else
|
2008-08-13 02:24:06 +02:00
|
|
|
Client_SetUser(Client, Req->argv[0], false);
|
2004-02-04 20:56:04 +01:00
|
|
|
#endif
|
2010-07-11 17:03:43 +02:00
|
|
|
Client_SetOrigUser(Client, Req->argv[0]);
|
2004-02-04 20:56:04 +01:00
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
/* "Real name" or user info text: Don't set it to the empty
|
|
|
|
* string, the original ircd can't deal with such "real names"
|
|
|
|
* (e. g. "USER user * * :") ... */
|
|
|
|
if (*Req->argv[3])
|
|
|
|
Client_SetInfo(Client, Req->argv[3]);
|
|
|
|
else
|
|
|
|
Client_SetInfo(Client, "-");
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
LogDebug("Connection %d: got valid USER command ...",
|
|
|
|
Client_Conn(Client));
|
|
|
|
if (Client_Type(Client) == CLIENT_GOTNICK)
|
|
|
|
return Hello_User(Client);
|
|
|
|
else
|
|
|
|
Client_SetType(Client, CLIENT_GOTUSER);
|
2002-02-28 00:26:21 +01:00
|
|
|
return CONNECTED;
|
2008-08-13 02:24:06 +02:00
|
|
|
|
|
|
|
} else if (Client_Type(Client) == CLIENT_SERVER ||
|
|
|
|
Client_Type(Client) == CLIENT_SERVICE) {
|
|
|
|
/* Server/service updating an user */
|
|
|
|
if (Req->argc != 4)
|
|
|
|
return IRC_WriteStrClient(Client,
|
|
|
|
ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client),
|
|
|
|
Req->command);
|
|
|
|
c = Client_Search(Req->prefix);
|
|
|
|
if (!c)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
|
|
|
|
Client_ID(Client),
|
|
|
|
Req->prefix);
|
|
|
|
|
|
|
|
Client_SetUser(c, Req->argv[0], true);
|
2010-07-11 17:03:43 +02:00
|
|
|
Client_SetOrigUser(c, Req->argv[0]);
|
2008-08-13 02:24:06 +02:00
|
|
|
Client_SetHostname(c, Req->argv[1]);
|
|
|
|
Client_SetInfo(c, Req->argv[3]);
|
|
|
|
|
|
|
|
LogDebug("Connection %d: got valid USER command for \"%s\".",
|
|
|
|
Client_Conn(Client), Client_Mask(c));
|
2008-08-13 21:55:22 +02:00
|
|
|
|
2008-08-16 01:24:35 +02:00
|
|
|
/* RFC 1459 style user registration?
|
|
|
|
* Introduce client to network: */
|
|
|
|
if (Client_Type(c) == CLIENT_GOTNICK)
|
2008-08-17 17:27:45 +02:00
|
|
|
Introduce_Client(Client, c, CLIENT_USER);
|
2008-08-13 21:55:22 +02:00
|
|
|
|
2008-08-13 02:24:06 +02:00
|
|
|
return CONNECTED;
|
|
|
|
} else if (Client_Type(Client) == CLIENT_USER) {
|
|
|
|
/* Already registered connection */
|
|
|
|
return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
|
|
|
|
Client_ID(Client));
|
|
|
|
} else {
|
|
|
|
/* Unexpected/invalid connection state? */
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
|
|
|
|
Client_ID(Client));
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
} /* IRC_USER */
|
|
|
|
|
|
|
|
|
2008-05-05 18:06:43 +02:00
|
|
|
/**
|
2010-12-29 14:09:46 +01:00
|
|
|
* Handler for the IRC "SERVICE" command.
|
|
|
|
*
|
2008-08-17 17:29:41 +02:00
|
|
|
* This function implements IRC Services registration using the SERVICE command
|
|
|
|
* defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
|
2010-12-29 14:09:46 +01:00
|
|
|
*
|
2008-08-17 17:29:41 +02:00
|
|
|
* At the moment ngIRCd doesn't support directly linked services, so this
|
|
|
|
* function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
|
|
|
|
* received from a peer server.
|
2010-12-29 14:09:46 +01:00
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED..
|
2008-05-05 18:06:43 +02:00
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_SERVICE(CLIENT *Client, REQUEST *Req)
|
|
|
|
{
|
2008-08-17 17:29:41 +02:00
|
|
|
CLIENT *c, *intr_c;
|
|
|
|
char *nick, *user, *host, *info, *modes, *ptr;
|
|
|
|
int token, hops;
|
|
|
|
|
2008-05-05 18:06:43 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
|
|
|
|
2008-08-17 17:29:41 +02:00
|
|
|
if (Client_Type(Client) != CLIENT_GOTPASS &&
|
|
|
|
Client_Type(Client) != CLIENT_SERVER)
|
2008-05-05 18:06:43 +02:00
|
|
|
return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
|
|
|
|
Client_ID(Client));
|
|
|
|
|
|
|
|
if (Req->argc != 6)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
|
2008-08-17 17:29:41 +02:00
|
|
|
if (Client_Type(Client) != CLIENT_SERVER)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
|
2008-05-05 18:06:43 +02:00
|
|
|
Client_ID(Client), Req->argv[0]);
|
2008-08-17 17:29:41 +02:00
|
|
|
|
|
|
|
/* Bad number of parameters? */
|
|
|
|
if (Req->argc != 6)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
|
|
|
|
nick = Req->argv[0];
|
|
|
|
user = NULL; host = NULL;
|
|
|
|
token = atoi(Req->argv[1]);
|
|
|
|
hops = atoi(Req->argv[4]);
|
|
|
|
info = Req->argv[5];
|
|
|
|
|
|
|
|
/* Validate service name ("nick name") */
|
|
|
|
c = Client_Search(nick);
|
|
|
|
if(c) {
|
|
|
|
/* Nick name collission: disconnect (KILL) both clients! */
|
|
|
|
Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
|
|
|
|
Client_ID(Client), nick);
|
|
|
|
Kill_Nick(nick, "Nick collision");
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the server to which the service is connected */
|
|
|
|
intr_c = Client_GetFromToken(Client, token);
|
|
|
|
if (! intr_c) {
|
|
|
|
Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
|
|
|
|
Client_ID(Client), nick);
|
|
|
|
Kill_Nick(nick, "Unknown server");
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get user and host name */
|
|
|
|
ptr = strchr(nick, '@');
|
|
|
|
if (ptr) {
|
|
|
|
*ptr = '\0';
|
|
|
|
host = ++ptr;
|
|
|
|
}
|
|
|
|
if (!host)
|
|
|
|
host = Client_Hostname(intr_c);
|
|
|
|
ptr = strchr(nick, '!');
|
|
|
|
if (ptr) {
|
|
|
|
*ptr = '\0';
|
|
|
|
user = ++ptr;
|
|
|
|
}
|
|
|
|
if (!user)
|
|
|
|
user = nick;
|
|
|
|
|
|
|
|
/* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
|
|
|
|
* for future usage"; but we use it to transfer the modes and check
|
|
|
|
* that the first character is a '+' sign and ignore it otherwise. */
|
|
|
|
modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
|
|
|
|
|
|
|
|
c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
|
|
|
|
token, modes, info, true);
|
|
|
|
if (! c) {
|
|
|
|
/* Couldn't create client structure, so KILL the service to
|
|
|
|
* keep network status consistent ... */
|
|
|
|
Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
|
|
|
|
Client_Conn(Client));
|
|
|
|
Kill_Nick(nick, "Server error");
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
Introduce_Client(Client, c, CLIENT_SERVICE);
|
|
|
|
return CONNECTED;
|
2008-05-05 18:06:43 +02:00
|
|
|
} /* IRC_SERVICE */
|
|
|
|
|
|
|
|
|
2010-02-11 00:01:53 +01:00
|
|
|
/**
|
2010-12-29 14:10:18 +01:00
|
|
|
* Handler for the IRC "WEBIRC" command.
|
|
|
|
*
|
|
|
|
* See doc/Protocol.txt, section II.4:
|
|
|
|
* "Update webchat/proxy client information".
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
2010-02-11 00:01:53 +01:00
|
|
|
*/
|
|
|
|
GLOBAL bool
|
|
|
|
IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
|
|
|
|
{
|
|
|
|
/* Exactly 4 parameters are requited */
|
|
|
|
if (Req->argc != 4)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
|
|
|
|
if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
|
|
|
|
Client_ID(Client));
|
|
|
|
|
|
|
|
LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
|
|
|
|
Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
|
|
|
|
|
|
|
|
Client_SetUser(Client, Req->argv[1], true);
|
2010-07-11 17:03:43 +02:00
|
|
|
Client_SetOrigUser(Client, Req->argv[1]);
|
2010-02-11 00:01:53 +01:00
|
|
|
Client_SetHostname(Client, Req->argv[2]);
|
|
|
|
return CONNECTED;
|
|
|
|
} /* IRC_WEBIRC */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Handler for the IRC "QUIT" command.
|
|
|
|
*
|
|
|
|
* See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2002-05-27 15:09:26 +02:00
|
|
|
IRC_QUIT( CLIENT *Client, REQUEST *Req )
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
|
|
|
CLIENT *target;
|
2005-06-04 14:32:09 +02:00
|
|
|
char quitmsg[LINE_LEN];
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
assert( Client != NULL );
|
|
|
|
assert( Req != NULL );
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2005-06-04 14:32:09 +02:00
|
|
|
/* Wrong number of arguments? */
|
|
|
|
if( Req->argc > 1 )
|
|
|
|
return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
|
|
|
|
|
|
|
|
if (Req->argc == 1)
|
|
|
|
strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2002-04-14 16:02:35 +02:00
|
|
|
if ( Client_Type( Client ) == CLIENT_SERVER )
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
|
|
|
/* Server */
|
|
|
|
target = Client_Search( Req->prefix );
|
|
|
|
if( ! target )
|
|
|
|
{
|
2002-03-11 23:06:32 +01:00
|
|
|
Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
|
2002-02-28 00:26:21 +01:00
|
|
|
return CONNECTED;
|
|
|
|
}
|
|
|
|
|
2005-06-04 14:32:09 +02:00
|
|
|
Client_Destroy( target, "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
return CONNECTED;
|
|
|
|
}
|
2002-04-14 16:02:35 +02:00
|
|
|
else
|
|
|
|
{
|
2005-06-04 14:32:09 +02:00
|
|
|
if (Req->argc == 1 && quitmsg[0] != '\"') {
|
|
|
|
/* " " to avoid confusion */
|
|
|
|
strlcpy(quitmsg, "\"", sizeof quitmsg);
|
|
|
|
strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
|
|
|
|
strlcat(quitmsg, "\"", sizeof quitmsg );
|
|
|
|
}
|
2002-04-14 16:02:35 +02:00
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* User, Service, or not yet registered */
|
2005-06-04 14:32:09 +02:00
|
|
|
Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
|
2008-08-13 01:55:32 +02:00
|
|
|
|
2002-04-14 16:02:35 +02:00
|
|
|
return DISCONNECTED;
|
|
|
|
}
|
2002-02-28 00:26:21 +01:00
|
|
|
} /* IRC_QUIT */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Handler for the IRC "PING" command.
|
|
|
|
*
|
|
|
|
* See RFC 2812, 3.7.2 "Ping message".
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2010-12-20 02:01:25 +01:00
|
|
|
GLOBAL bool
|
|
|
|
IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
|
|
|
|
{
|
|
|
|
Req->argc = 0;
|
|
|
|
return IRC_QUIT(Client, Req);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2005-08-28 13:40:13 +02:00
|
|
|
IRC_PING(CLIENT *Client, REQUEST *Req)
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
|
|
|
CLIENT *target, *from;
|
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
if (Req->argc < 1)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
|
|
|
|
Client_ID(Client));
|
2002-03-10 23:40:22 +01:00
|
|
|
#ifdef STRICT_RFC
|
2005-08-28 13:40:13 +02:00
|
|
|
/* Don't ignore additional arguments when in "strict" mode */
|
|
|
|
if (Req->argc > 2)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
2002-03-10 23:40:22 +01:00
|
|
|
#endif
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
if (Req->argc > 1) {
|
|
|
|
/* A target has been specified ... */
|
|
|
|
target = Client_Search(Req->argv[1]);
|
|
|
|
|
|
|
|
if (!target || Client_Type(target) != CLIENT_SERVER)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
|
|
|
|
Client_ID(Client), Req->argv[1]);
|
|
|
|
|
|
|
|
if (target != Client_ThisServer()) {
|
|
|
|
/* Ok, we have to forward the PING */
|
|
|
|
if (Client_Type(Client) == CLIENT_SERVER)
|
|
|
|
from = Client_Search(Req->prefix);
|
2005-08-27 20:39:56 +02:00
|
|
|
else
|
|
|
|
from = Client;
|
|
|
|
if (!from)
|
2005-08-28 13:40:13 +02:00
|
|
|
return IRC_WriteStrClient(Client,
|
|
|
|
ERR_NOSUCHSERVER_MSG,
|
|
|
|
Client_ID(Client), Req->prefix);
|
|
|
|
|
2005-08-27 20:39:56 +02:00
|
|
|
return IRC_WriteStrClientPrefix(target, from,
|
2005-08-28 13:40:13 +02:00
|
|
|
"PING %s :%s", Req->argv[0],
|
|
|
|
Req->argv[1] );
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
if (Client_Type(Client) == CLIENT_SERVER) {
|
|
|
|
if (Req->prefix)
|
|
|
|
from = Client_Search(Req->prefix);
|
|
|
|
else
|
|
|
|
from = Client;
|
|
|
|
} else
|
|
|
|
from = Client_ThisServer();
|
|
|
|
if (!from)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
|
|
|
|
Client_ID(Client), Req->prefix);
|
|
|
|
|
|
|
|
Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
|
|
|
|
Client_Conn(Client));
|
|
|
|
|
2005-08-27 20:39:56 +02:00
|
|
|
#ifdef STRICT_RFC
|
2005-08-28 13:40:13 +02:00
|
|
|
return IRC_WriteStrClient(Client, "PONG %s :%s",
|
|
|
|
Client_ID(from), Client_ID(Client));
|
2005-08-27 20:39:56 +02:00
|
|
|
#else
|
2005-08-28 13:40:13 +02:00
|
|
|
/* Some clients depend on the argument being returned in the PONG
|
2008-04-20 23:10:22 +02:00
|
|
|
* reply (not mentioned in any RFC, though) */
|
2005-08-28 13:40:13 +02:00
|
|
|
return IRC_WriteStrClient(Client, "PONG %s :%s",
|
|
|
|
Client_ID(from), Req->argv[0]);
|
|
|
|
#endif
|
2002-02-28 00:26:21 +01:00
|
|
|
} /* IRC_PING */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Handler for the IRC "PONG" command.
|
|
|
|
*
|
|
|
|
* See RFC 2812, 3.7.3 "Pong message".
|
|
|
|
*
|
|
|
|
* @param Client The client from which this command has been received.
|
|
|
|
* @param Req Request structure with prefix and all parameters.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
2005-08-28 13:40:13 +02:00
|
|
|
IRC_PONG(CLIENT *Client, REQUEST *Req)
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
|
|
|
CLIENT *target, *from;
|
2005-08-28 13:40:13 +02:00
|
|
|
char *s;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
assert(Client != NULL);
|
|
|
|
assert(Req != NULL);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
/* Wrong number of arguments? */
|
|
|
|
if (Req->argc < 1)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
|
|
|
|
Client_ID(Client));
|
|
|
|
if (Req->argc > 2)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
|
|
|
|
Client_ID(Client), Req->command);
|
|
|
|
|
|
|
|
/* Forward? */
|
|
|
|
if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
|
|
|
|
target = Client_Search(Req->argv[0]);
|
|
|
|
if (!target)
|
|
|
|
return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
|
|
|
|
Client_ID(Client), Req->argv[0]);
|
|
|
|
|
2005-09-01 12:51:24 +02:00
|
|
|
from = Client_Search(Req->prefix);
|
|
|
|
|
|
|
|
if (target != Client_ThisServer() && target != from) {
|
2005-08-28 13:40:13 +02:00
|
|
|
/* Ok, we have to forward the message. */
|
|
|
|
if (!from)
|
|
|
|
return IRC_WriteStrClient(Client,
|
|
|
|
ERR_NOSUCHSERVER_MSG,
|
|
|
|
Client_ID(Client), Req->prefix);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
|
|
|
|
s = Client_ID(from);
|
|
|
|
else
|
|
|
|
s = Req->argv[0];
|
|
|
|
return IRC_WriteStrClientPrefix(target, from,
|
|
|
|
"PONG %s :%s", s, Req->argv[1]);
|
2002-02-28 00:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-28 13:40:13 +02:00
|
|
|
/* The connection timestamp has already been updated when the data has
|
|
|
|
* been read from so socket, so we don't need to update it here. */
|
2009-01-10 00:44:34 +01:00
|
|
|
#ifdef DEBUG
|
2005-08-28 13:40:13 +02:00
|
|
|
if (Client_Conn(Client) > NONE)
|
|
|
|
Log(LOG_DEBUG,
|
|
|
|
"Connection %d: received PONG. Lag: %ld seconds.",
|
|
|
|
Client_Conn(Client),
|
|
|
|
time(NULL) - Conn_LastPing(Client_Conn(Client)));
|
|
|
|
else
|
|
|
|
Log(LOG_DEBUG,
|
|
|
|
"Connection %d: received PONG.", Client_Conn(Client));
|
2009-01-10 00:44:34 +01:00
|
|
|
#endif
|
2002-02-28 00:26:21 +01:00
|
|
|
return CONNECTED;
|
|
|
|
} /* IRC_PONG */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Initiate client registration.
|
|
|
|
*
|
|
|
|
* This function is called after the daemon received the required NICK and
|
|
|
|
* USER commands of a new client. If the daemon is compiled with support for
|
|
|
|
* PAM, the authentication sub-processs is forked; otherwise the global server
|
|
|
|
* password is checked.
|
|
|
|
*
|
|
|
|
* @param Client The client logging in.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool
|
2008-04-20 23:10:22 +02:00
|
|
|
Hello_User(CLIENT * Client)
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2010-07-12 12:56:33 +02:00
|
|
|
#ifdef PAM
|
|
|
|
int pipefd[2], result;
|
|
|
|
CONN_ID conn;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
assert(Client != NULL);
|
|
|
|
conn = Client_Conn(Client);
|
|
|
|
|
config: deprecate NoXX-Options
ngircd unfortunately uses several options using double-negation, e.g.
NoIdent = No, NoPam = No, etc.
This renames all options by dropping the "No" prefix, e.g.
"NoIdent = no" becomes "Ident = yes".
The old options will continue to work, but will cause a warning
message.
Also update man pages and default config.
To prevent silly
'Ident = yes' from appearing in --configtest output in the
'ident support not compiled in and Ident Option not used' case,
make default value depend on feature availability.
If feature is available, enable by default, otherwise disable.
We might consider moving these options to a new
[Feature]
section, or something like that, because none of these options are
essential.
Another possible improvement:
'Ident = yes' option in ngircd.conf causes a warning if ngircd was
built without ident support.
This does not happen with e.g. zeroconf....
2011-01-08 15:56:14 +01:00
|
|
|
if (!Conf_PAM) {
|
2010-07-13 22:14:53 +02:00
|
|
|
/* Don't do any PAM authentication at all, instead emulate
|
|
|
|
* the beahiour of the daemon compiled without PAM support:
|
|
|
|
* because there can't be any "server password", all
|
|
|
|
* passwords supplied are classified as "wrong". */
|
|
|
|
if(Client_Password(Client)[0] == '\0')
|
|
|
|
return Hello_User_PostAuth(Client);
|
|
|
|
Reject_Client(Client);
|
|
|
|
return DISCONNECTED;
|
|
|
|
}
|
|
|
|
|
2010-07-14 10:29:05 +02:00
|
|
|
/* Fork child process for PAM authentication; and make sure that the
|
|
|
|
* process timeout is set higher than the login timeout! */
|
|
|
|
pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
|
|
|
|
cb_Read_Auth_Result, Conf_PongTimeout + 1);
|
2010-07-12 12:56:33 +02:00
|
|
|
if (pid > 0) {
|
|
|
|
LogDebug("Authenticator for connection %d created (PID %d).",
|
|
|
|
conn, pid);
|
|
|
|
return CONNECTED;
|
|
|
|
} else {
|
|
|
|
/* Sub process */
|
|
|
|
Log_Init_Subprocess("Auth");
|
2010-07-13 22:14:53 +02:00
|
|
|
result = PAM_Authenticate(Client);
|
2011-02-23 22:55:45 +01:00
|
|
|
if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
|
|
|
|
Log_Subprocess(LOG_ERR,
|
|
|
|
"Failed to pipe result to parent!");
|
2010-07-12 12:56:33 +02:00
|
|
|
Log_Exit_Subprocess("Auth");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
#else
|
2008-04-20 23:10:22 +02:00
|
|
|
assert(Client != NULL);
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2010-07-12 12:56:33 +02:00
|
|
|
/* Check global server password ... */
|
2008-04-20 23:10:22 +02:00
|
|
|
if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
|
2003-12-04 15:05:16 +01:00
|
|
|
/* Bad password! */
|
2010-07-12 12:56:33 +02:00
|
|
|
Reject_Client(Client);
|
2002-02-28 00:26:21 +01:00
|
|
|
return DISCONNECTED;
|
|
|
|
}
|
2010-07-12 12:56:33 +02:00
|
|
|
return Hello_User_PostAuth(Client);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2010-07-12 12:56:33 +02:00
|
|
|
#ifdef PAM
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read result of the authenticatior sub-process from pipe
|
2010-12-29 14:09:46 +01:00
|
|
|
*
|
|
|
|
* @param r_fd File descriptor of the pipe.
|
|
|
|
* @param events (ignored IO specification)
|
2010-07-12 12:56:33 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
cb_Read_Auth_Result(int r_fd, UNUSED short events)
|
|
|
|
{
|
|
|
|
CONN_ID conn;
|
|
|
|
CLIENT *client;
|
|
|
|
int result;
|
|
|
|
size_t len;
|
|
|
|
PROC_STAT *proc;
|
|
|
|
|
|
|
|
LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
|
|
|
|
conn = Conn_GetFromProc(r_fd);
|
|
|
|
if (conn == NONE) {
|
|
|
|
/* Ops, none found? Probably the connection has already
|
|
|
|
* been closed!? We'll ignore that ... */
|
|
|
|
io_close(r_fd);
|
|
|
|
LogDebug("Auth: Got callback for unknown connection!?");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
proc = Conn_GetProcStat(conn);
|
|
|
|
client = Conn_GetClient(conn);
|
|
|
|
|
|
|
|
/* Read result from pipe */
|
|
|
|
len = Proc_Read(proc, &result, sizeof(result));
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (len != sizeof(result)) {
|
|
|
|
Log(LOG_CRIT, "Auth: Got malformed result!");
|
|
|
|
Reject_Client(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-07-13 23:18:54 +02:00
|
|
|
if (result == true) {
|
|
|
|
Client_SetUser(client, Client_OrigUser(client), true);
|
2010-07-12 12:56:33 +02:00
|
|
|
(void)Hello_User_PostAuth(client);
|
2010-07-13 23:18:54 +02:00
|
|
|
} else
|
2010-07-12 12:56:33 +02:00
|
|
|
Reject_Client(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Reject a client because of wrong password.
|
|
|
|
*
|
|
|
|
* This function is called either when the global server password or a password
|
|
|
|
* checked using PAM has been wrong.
|
|
|
|
*
|
|
|
|
* @param Client The client to reject.
|
|
|
|
*/
|
2010-07-12 12:56:33 +02:00
|
|
|
static void
|
|
|
|
Reject_Client(CLIENT *Client)
|
|
|
|
{
|
|
|
|
Log(LOG_ERR,
|
|
|
|
"User \"%s\" rejected (connection %d): Access denied!",
|
|
|
|
Client_Mask(Client), Client_Conn(Client));
|
|
|
|
Conn_Close(Client_Conn(Client), NULL,
|
|
|
|
"Access denied! Bad password?", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Finish client registration.
|
|
|
|
*
|
|
|
|
* Introduce the new client to the network and send all "hello messages"
|
|
|
|
* to it after authentication has been succeeded.
|
|
|
|
*
|
|
|
|
* @param Client The client logging in.
|
|
|
|
* @returns CONNECTED or DISCONNECTED.
|
|
|
|
*/
|
2010-07-12 12:56:33 +02:00
|
|
|
static bool
|
|
|
|
Hello_User_PostAuth(CLIENT *Client)
|
|
|
|
{
|
2008-08-17 17:27:45 +02:00
|
|
|
Introduce_Client(NULL, Client, CLIENT_USER);
|
2008-04-20 23:10:22 +02:00
|
|
|
|
|
|
|
if (!IRC_WriteStrClient
|
|
|
|
(Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
|
|
|
|
return false;
|
|
|
|
if (!IRC_WriteStrClient
|
|
|
|
(Client, RPL_YOURHOST_MSG, Client_ID(Client),
|
|
|
|
Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
|
|
|
|
TARGET_VENDOR, TARGET_OS))
|
|
|
|
return false;
|
|
|
|
if (!IRC_WriteStrClient
|
|
|
|
(Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
|
|
|
|
return false;
|
|
|
|
if (!IRC_WriteStrClient
|
|
|
|
(Client, RPL_MYINFO_MSG, Client_ID(Client),
|
|
|
|
Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
|
|
|
|
CHANMODES))
|
|
|
|
return false;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2006-10-01 21:03:05 +02:00
|
|
|
/* Features supported by this server (005 numeric, ISUPPORT),
|
|
|
|
* see <http://www.irc.org/tech_docs/005.html> for details. */
|
2008-04-20 23:10:22 +02:00
|
|
|
if (!IRC_Send_ISUPPORT(Client))
|
2006-10-01 21:03:05 +02:00
|
|
|
return DISCONNECTED;
|
2002-12-23 00:31:21 +01:00
|
|
|
|
2008-04-20 23:10:22 +02:00
|
|
|
if (!IRC_Send_LUSERS(Client))
|
|
|
|
return DISCONNECTED;
|
|
|
|
if (!IRC_Show_MOTD(Client))
|
|
|
|
return DISCONNECTED;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2003-11-06 00:24:48 +01:00
|
|
|
/* Suspend the client for a second ... */
|
2008-04-20 23:10:22 +02:00
|
|
|
IRC_SetPenalty(Client, 1);
|
2003-11-06 00:24:48 +01:00
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
return CONNECTED;
|
2010-07-12 12:56:33 +02:00
|
|
|
}
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Kill all users with a specific nick name in the network.
|
|
|
|
*
|
|
|
|
* @param Nick Nick name.
|
|
|
|
* @param Reason Reason for the KILL.
|
|
|
|
*/
|
2005-07-31 22:13:07 +02:00
|
|
|
static void
|
2005-03-19 19:43:48 +01:00
|
|
|
Kill_Nick( char *Nick, char *Reason )
|
2002-02-28 00:26:21 +01:00
|
|
|
{
|
2003-01-08 23:28:12 +01:00
|
|
|
REQUEST r;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
|
|
|
assert( Nick != NULL );
|
|
|
|
assert( Reason != NULL );
|
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
r.prefix = (char *)Client_ThisServer( );
|
2003-01-08 23:28:12 +01:00
|
|
|
r.argv[0] = Nick;
|
|
|
|
r.argv[1] = Reason;
|
|
|
|
r.argc = 2;
|
2002-02-28 00:26:21 +01:00
|
|
|
|
2003-01-08 23:28:12 +01:00
|
|
|
Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason );
|
|
|
|
IRC_KILL( Client_ThisServer( ), &r );
|
2002-02-28 00:26:21 +01:00
|
|
|
} /* Kill_Nick */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Introduce a new user or service client in the network.
|
|
|
|
*
|
|
|
|
* @param From Remote server introducing the client or NULL (local).
|
|
|
|
* @param Client New client.
|
|
|
|
* @param Type Type of the client (CLIENT_USER or CLIENT_SERVICE).
|
|
|
|
*/
|
2008-08-13 17:06:26 +02:00
|
|
|
static void
|
2008-08-17 17:27:45 +02:00
|
|
|
Introduce_Client(CLIENT *From, CLIENT *Client, int Type)
|
2008-08-13 17:06:26 +02:00
|
|
|
{
|
2008-08-17 17:27:45 +02:00
|
|
|
/* Set client type (user or service) */
|
|
|
|
Client_SetType(Client, Type);
|
2008-08-16 01:24:35 +02:00
|
|
|
|
|
|
|
if (From) {
|
2008-08-17 17:27:45 +02:00
|
|
|
if (Conf_IsService(Conf_GetServer(Client_Conn(From)),
|
|
|
|
Client_ID(Client)))
|
2008-08-16 17:52:02 +02:00
|
|
|
Client_SetType(Client, CLIENT_SERVICE);
|
2008-08-16 02:36:30 +02:00
|
|
|
LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
|
2008-08-17 17:27:45 +02:00
|
|
|
Client_TypeText(Client), Client_Mask(Client),
|
|
|
|
Client_Modes(Client), Client_ID(From),
|
|
|
|
Client_ID(Client_Introducer(Client)),
|
2008-08-16 01:24:35 +02:00
|
|
|
Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
|
2010-05-22 17:19:24 +02:00
|
|
|
} else {
|
2008-08-17 17:27:45 +02:00
|
|
|
Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
|
|
|
|
Client_TypeText(Client), Client_Mask(Client),
|
|
|
|
Client_Conn(Client));
|
2010-05-22 17:19:24 +02:00
|
|
|
Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
|
|
|
|
Client_ID(Client), Client_User(Client),
|
|
|
|
Client_Hostname(Client),
|
|
|
|
Conn_IPA(Client_Conn(Client)),
|
|
|
|
Client_TypeText(Client));
|
|
|
|
}
|
2008-08-16 01:24:35 +02:00
|
|
|
|
|
|
|
/* Inform other servers */
|
2008-08-13 21:55:22 +02:00
|
|
|
IRC_WriteStrServersPrefixFlag_CB(From,
|
|
|
|
From != NULL ? From : Client_ThisServer(),
|
2008-08-16 00:02:44 +02:00
|
|
|
'\0', cb_introduceClient, (void *)Client);
|
2008-08-13 17:06:26 +02:00
|
|
|
} /* Introduce_Client */
|
|
|
|
|
|
|
|
|
2010-12-29 14:09:46 +01:00
|
|
|
/**
|
|
|
|
* Introduce a new user or service client to a remote server.
|
|
|
|
*
|
|
|
|
* This function differentiates between RFC1459 and RFC2813 server links and
|
|
|
|
* generates the appropriate commands to register the new user or service.
|
|
|
|
*
|
|
|
|
* @param To The remote server to inform.
|
|
|
|
* @param Prefix Prefix for the generated commands.
|
|
|
|
* @param data CLIENT structure of the new client.
|
|
|
|
*/
|
2008-08-13 21:55:22 +02:00
|
|
|
static void
|
2008-08-16 00:02:44 +02:00
|
|
|
cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
|
2008-08-13 21:55:22 +02:00
|
|
|
{
|
2008-08-16 00:02:44 +02:00
|
|
|
CLIENT *c = (CLIENT *)data;
|
2008-08-13 21:55:22 +02:00
|
|
|
CONN_ID conn;
|
2008-08-16 00:02:44 +02:00
|
|
|
char *modes, *user, *host;
|
|
|
|
|
|
|
|
modes = Client_Modes(c);
|
|
|
|
user = Client_User(c) ? Client_User(c) : "-";
|
|
|
|
host = Client_Hostname(c) ? Client_Hostname(c) : "-";
|
2008-08-13 21:55:22 +02:00
|
|
|
|
2008-08-16 00:02:44 +02:00
|
|
|
conn = Client_Conn(To);
|
2008-08-13 21:55:22 +02:00
|
|
|
if (Conn_Options(conn) & CONN_RFC1459) {
|
|
|
|
/* RFC 1459 mode: separate NICK and USER commands */
|
2008-08-16 00:02:44 +02:00
|
|
|
Conn_WriteStr(conn, "NICK %s :%d", Client_ID(c),
|
|
|
|
Client_Hops(c) + 1);
|
2008-08-13 21:55:22 +02:00
|
|
|
Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
|
2008-08-16 00:02:44 +02:00
|
|
|
Client_ID(c), user, host,
|
|
|
|
Client_ID(Client_Introducer(c)), Client_Info(c));
|
|
|
|
if (modes[0])
|
2008-08-15 00:11:12 +02:00
|
|
|
Conn_WriteStr(conn, ":%s MODE %s +%s",
|
2008-08-16 00:02:44 +02:00
|
|
|
Client_ID(c), Client_ID(c), modes);
|
2008-08-13 21:55:22 +02:00
|
|
|
} else {
|
2008-08-17 17:29:41 +02:00
|
|
|
/* RFC 2813 mode: one combined NICK or SERVICE command */
|
|
|
|
if (Client_Type(c) == CLIENT_SERVICE
|
|
|
|
&& strchr(Client_Flags(To), 'S'))
|
|
|
|
IRC_WriteStrClientPrefix(To, Prefix,
|
|
|
|
"SERVICE %s %d * +%s %d :%s",
|
|
|
|
Client_Mask(c),
|
|
|
|
Client_MyToken(Client_Introducer(c)),
|
|
|
|
Client_Modes(c), Client_Hops(c) + 1,
|
|
|
|
Client_Info(c));
|
|
|
|
else
|
|
|
|
IRC_WriteStrClientPrefix(To, Prefix,
|
2008-08-15 00:11:12 +02:00
|
|
|
"NICK %s %d %s %s %d +%s :%s",
|
2008-08-16 00:02:44 +02:00
|
|
|
Client_ID(c), Client_Hops(c) + 1,
|
|
|
|
user, host,
|
|
|
|
Client_MyToken(Client_Introducer(c)),
|
|
|
|
modes, Client_Info(c));
|
2008-08-13 21:55:22 +02:00
|
|
|
}
|
|
|
|
} /* cb_introduceClient */
|
|
|
|
|
|
|
|
|
2002-02-28 00:26:21 +01:00
|
|
|
/* -eof- */
|