Beispiel #1
0
/**
 * Send information about changed channel invite/ban/exception lists to clients.
 *
 * @param IsAdd true if the list item has been added, false otherwise.
 * @param ModeChar The mode to use (e. g. 'b' or 'I')
 * @param Prefix The originator of the mode list change.
 * @param Client The sender of the command.
 * @param Channel The channel of which the list has been modified.
 * @param Mask The mask which has been added or removed.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix,
		CLIENT *Client, CHANNEL *Channel, const char *Mask)
{
	bool ok = true;

	/* Send confirmation to the client */
	if (Client_Type(Client) == CLIENT_USER)
		ok = IRC_WriteStrClientPrefix(Client, Prefix, "MODE %s %c%c %s",
					      Channel_Name(Channel),
					      IsAdd ? '+' : '-',
					      ModeChar, Mask);

	/* to other servers */
	IRC_WriteStrServersPrefix(Client, Prefix, "MODE %s %c%c %s",
				  Channel_Name(Channel), IsAdd ? '+' : '-',
				  ModeChar, Mask);

	/* and local users in channel */
	IRC_WriteStrChannelPrefix(Client, Channel, Prefix, false,
				  "MODE %s %c%c %s", Channel_Name(Channel),
				  IsAdd ? '+' : '-', ModeChar, Mask );

	return ok;
} /* Send_ListChange */
Beispiel #2
0
/**
 * 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;
	CONN_ID my_conn, conn;
	CLIENT *c;

	/* Do we know such a client in the network? */
	c = Client_Search(Nick);
	if (!c) {
		LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick);
		return CONNECTED;
	}

	/* Inform other servers */
	IRC_WriteStrServersPrefix(From ? Client : NULL,
				  From ? From : Client_ThisServer(),
				  "KILL %s :%s", Nick, Reason);

	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 */
	my_conn = Client_Conn(Client);

	/* 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;
}
Beispiel #3
0
/**
 * Destroy user or service client.
 */
static void
Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool SendQuit)
{
	if(Client->conn_id != NONE) {
		/* Local (directly connected) client */
		Log(LOG_NOTICE,
		    "%s \"%s\" unregistered (connection %d): %s.",
		    Client_TypeText(Client), Client_Mask(Client),
		    Client->conn_id, Txt);
		Log_ServerNotice('c', "Client exiting: %s (%s@%s) [%s]",
			         Client_ID(Client), Client_User(Client),
				 Client_Hostname(Client), Txt);

		if (SendQuit) {
			/* Inforam all the other servers */
			if (FwdMsg)
				IRC_WriteStrServersPrefix(NULL,
						Client, "QUIT :%s", FwdMsg );
			else
				IRC_WriteStrServersPrefix(NULL,
						Client, "QUIT :");
		}
	} else {
		/* Remote client */
		LogDebug("%s \"%s\" unregistered: %s.",
			 Client_TypeText(Client), Client_Mask(Client), Txt);

		if(SendQuit) {
			/* Inform all the other servers, but the ones in the
			 * direction we got the QUIT from */
			if(FwdMsg)
				IRC_WriteStrServersPrefix(Client_NextHop(Client),
						Client, "QUIT :%s", FwdMsg );
			else
				IRC_WriteStrServersPrefix(Client_NextHop(Client),
						Client, "QUIT :" );
		}
	}

	/* Unregister client from channels */
	Channel_Quit(Client, FwdMsg ? FwdMsg : Client->id);

	/* Register client in My_Whowas structure */
	Client_RegisterWhowas(Client);
} /* Destroy_UserOrService */
Beispiel #4
0
/**
 * Handler for the IRC "AWAY" 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_AWAY( CLIENT *Client, REQUEST *Req )
{
	assert (Client != NULL);
	assert (Req != NULL);

	if (Req->argc == 1 && Req->argv[0][0]) {
		Client_SetAway(Client, Req->argv[0]);
		Client_ModeAdd(Client, 'a');
		IRC_WriteStrServersPrefix(Client, Client, "MODE %s :+a",
					  Client_ID( Client));
		return IRC_WriteStrClient(Client, RPL_NOWAWAY_MSG,
					  Client_ID( Client));
	} else {
		Client_ModeDel(Client, 'a');
		IRC_WriteStrServersPrefix(Client, Client, "MODE %s :-a",
					  Client_ID( Client));
		return IRC_WriteStrClient(Client, RPL_UNAWAY_MSG,
					  Client_ID( Client));
	}
} /* IRC_AWAY */
Beispiel #5
0
/**
 * Handler for the IRC "KILL" command.
 *
 * This function implements the IRC command "KILL" wich is used to selectively
 * disconnect clients. It can be used by IRC operators and servers, for example
 * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
 *
 * Please note that this function is also called internally, without a real
 * KILL command being received over the network! Client is Client_ThisServer()
 * in this case, and the prefix in Req is NULL.
 *
 * @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.
 * @returns		CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_KILL( CLIENT *Client, REQUEST *Req )
{
	CLIENT *prefix, *c;
	char reason[COMMAND_LEN], *msg;
	CONN_ID my_conn, conn;

	assert (Client != NULL);
	assert (Req != NULL);

	if (Client_Type(Client) != CLIENT_SERVER && !Client_OperByMe(Client))
		return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
					  Client_ID(Client));

	if (Req->argc != 2)
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	/* 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
	 * prefix (origin) is invalid. */
	if (!prefix) {
		Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
		    Req->prefix );
		prefix = Client_ThisServer();
	}

	if (Client != Client_ThisServer())
		Log(LOG_NOTICE|LOG_snotice,
		    "Got KILL command from \"%s\" for \"%s\": %s",
		    Client_Mask(prefix), Req->argv[0], Req->argv[1]);

	/* 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]);
	else
		strlcpy(reason, Req->argv[1], sizeof(reason));

	/* Inform other servers */
	IRC_WriteStrServersPrefix(Client, prefix, "KILL %s :%s",
				  Req->argv[0], reason);

	/* Save ID of this connection */
	my_conn = Client_Conn( Client );

	/* Do we host such a client? */
	c = Client_Search( Req->argv[0] );
	if( c )
	{
		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_WriteStrClient( Client, msg,
					Client_ID( Client ));
			}

			Log( LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!",
			     Client_Type( c ), Req->argv[0] );
		}

		/* 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);
	}
	else
		Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] );

	/* Are we still connected or were we killed, too? */
	if(( my_conn > NONE ) && ( Conn_GetClient( my_conn )))
		return CONNECTED;
	else
		return DISCONNECTED;
} /* IRC_KILL */
Beispiel #6
0
/**
 * Handle channel mode and channel-user mode changes
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @param Origin The originator of the MODE command (prefix).
 * @param Channel The target channel of this MODE command.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
{
	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
	    argadd[CLIENT_PASS_LEN], *mode_ptr;
	bool connected, set, skiponce, retval, use_servermode,
	     is_halfop, is_op, is_admin, is_owner, is_machine, is_oper;
	int mode_arg, arg_arg, mode_arg_count = 0;
	CLIENT *client;
	long l;
	size_t len;

	is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false;

	if (Channel_IsModeless(Channel))
		return IRC_WriteErrClient(Client, ERR_NOCHANMODES_MSG,
				Client_ID(Client), Channel_Name(Channel));

	/* Mode request: let's answer it :-) */
	if (Req->argc <= 1)
		return Channel_Mode_Answer_Request(Origin, Channel);

	/* Check if origin is oper and opers can use mode */
	use_servermode = Conf_OperServerMode;
	if(Client_HasMode(Client, 'o') && Conf_OperCanMode) {
		is_oper = true;
	}

	/* Check if client is a server/service */
	if(Client_Type(Client) == CLIENT_SERVER ||
	   Client_Type(Client) == CLIENT_SERVICE) {
		is_machine = true;
	}

	/* Check if client is member of channel or an oper or an server/service */
	if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine)
		return IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG,
					  Client_ID(Origin),
					  Channel_Name(Channel));

	mode_arg = 1;
	mode_ptr = Req->argv[mode_arg];
	if (Req->argc > mode_arg + 1)
		arg_arg = mode_arg + 1;
	else
		arg_arg = -1;

	/* Initial state: set or unset modes? */
	skiponce = false;
	switch (*mode_ptr) {
	case '-':
		set = false;
		break;
	case '+':
		set = true;
		break;
	default:
		set = true;
		skiponce = true;
	}

	/* Prepare reply string */
	strcpy(the_modes, set ? "+" : "-");
	the_args[0] = '\0';

	x[1] = '\0';
	connected = CONNECTED;
	while (mode_ptr) {
		if (!skiponce)
			mode_ptr++;
		if (!*mode_ptr) {
			/* Try next argument if there's any */
			if (arg_arg < 0)
				break;
			if (arg_arg > mode_arg)
				mode_arg = arg_arg;
			else
				mode_arg++;

			if (mode_arg >= Req->argc)
				break;
			mode_ptr = Req->argv[mode_arg];

			if (Req->argc > mode_arg + 1)
				arg_arg = mode_arg + 1;
			else
				arg_arg = -1;
		}
		skiponce = false;

		switch (*mode_ptr) {
		case '+':
		case '-':
			if (((*mode_ptr == '+') && !set)
			    || ((*mode_ptr == '-') && set)) {
				/* Action modifier ("+"/"-") must be changed ... */
				len = strlen(the_modes) - 1;
				if (the_modes[len] == '+' || the_modes[len] == '-') {
					/* Adjust last action modifier in result */
					the_modes[len] = *mode_ptr;
				} else {
					/* Append modifier character to result string */
					x[0] = *mode_ptr;
					strlcat(the_modes, x, sizeof(the_modes));
				}
				set = *mode_ptr == '+';
			}
			continue;
		}

		/* Are there arguments left? */
		if (arg_arg >= Req->argc)
			arg_arg = -1;

		if(!is_machine && !is_oper) {
			if (Channel_UserHasMode(Channel, Client, 'q'))
				is_owner = true;
			if (Channel_UserHasMode(Channel, Client, 'a'))
				is_admin = true;
			if (Channel_UserHasMode(Channel, Client, 'o'))
				is_op = true;
			if (Channel_UserHasMode(Channel, Client, 'h'))
				is_halfop = true;
		}

		/* Validate modes */
		x[0] = '\0';
		argadd[0] = '\0';
		client = NULL;
		switch (*mode_ptr) {
		/* --- Channel modes --- */
		case 'R': /* Registered users only */
		case 's': /* Secret channel */
		case 'z': /* Secure connections only */
			if(!is_oper && !is_machine && !is_owner &&
			   !is_admin && !is_op) {
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin), Channel_Name(Channel));
				goto chan_exit;
			}
		case 'i': /* Invite only */
		case 'V': /* Invite disallow */
		case 'M': /* Only identified nicks can write */
		case 'm': /* Moderated */
		case 'n': /* Only members can write */
		case 'N': /* Can't change nick while on this channel */
		case 'Q': /* No kicks */
		case 't': /* Topic locked */
			if(is_oper || is_machine || is_owner ||
			   is_admin || is_op || is_halfop)
				x[0] = *mode_ptr;
			else
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin), Channel_Name(Channel));
			break;
		case 'k': /* Channel key */
			if (Mode_Limit_Reached(Client, mode_arg_count++))
				goto chan_exit;
			if (!set) {
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop) {
					x[0] = *mode_ptr;
					if (Channel_HasMode(Channel, 'k'))
						strlcpy(argadd, "*", sizeof(argadd));
					if (arg_arg > mode_arg)
						arg_arg++;
				} else
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				break;
			}
			if (arg_arg > mode_arg) {
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop) {
					Channel_ModeDel(Channel, 'k');
					Channel_SetKey(Channel,
						       Req->argv[arg_arg]);
					strlcpy(argadd, Channel_Key(Channel),
						sizeof(argadd));
					x[0] = *mode_ptr;
				} else {
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				}
				Req->argv[arg_arg][0] = '\0';
				arg_arg++;
			} else {
#ifdef STRICT_RFC
				/* Only send error message in "strict" mode,
				 * this is how ircd2.11 and others behave ... */
				connected = IRC_WriteErrClient(Origin,
					ERR_NEEDMOREPARAMS_MSG,
					Client_ID(Origin), Req->command);
#endif
				goto chan_exit;
			}
			break;
		case 'l': /* Member limit */
			if (Mode_Limit_Reached(Client, mode_arg_count++))
				goto chan_exit;
			if (!set) {
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop)
					x[0] = *mode_ptr;
				else
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				break;
			}
			if (arg_arg > mode_arg) {
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop) {
					l = atol(Req->argv[arg_arg]);
					if (l > 0 && l < 0xFFFF) {
						Channel_ModeDel(Channel, 'l');
						Channel_SetMaxUsers(Channel, l);
						snprintf(argadd, sizeof(argadd),
							 "%ld", l);
						x[0] = *mode_ptr;
					}
				} else {
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				}
				Req->argv[arg_arg][0] = '\0';
				arg_arg++;
			} else {
#ifdef STRICT_RFC
				/* Only send error message in "strict" mode,
				 * this is how ircd2.11 and others behave ... */
				connected = IRC_WriteErrClient(Origin,
					ERR_NEEDMOREPARAMS_MSG,
					Client_ID(Origin), Req->command);
#endif
				goto chan_exit;
			}
			break;
		case 'O': /* IRC operators only */
			if (set) {
				/* Only IRC operators are allowed to
				 * set the 'O' channel mode! */
				if(is_oper || is_machine)
					x[0] = 'O';
				else
					connected = IRC_WriteErrClient(Origin,
						ERR_NOPRIVILEGES_MSG,
						Client_ID(Origin));
			} else if(is_oper || is_machine || is_owner ||
				  is_admin || is_op)
				x[0] = 'O';
			else
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin),
					Channel_Name(Channel));
			break;
		case 'P': /* Persistent channel */
			if (set) {
				/* Only IRC operators are allowed to
				 * set the 'P' channel mode! */
				if(is_oper || is_machine)
					x[0] = 'P';
				else
					connected = IRC_WriteErrClient(Origin,
						ERR_NOPRIVILEGES_MSG,
						Client_ID(Origin));
			} else if(is_oper || is_machine || is_owner ||
				  is_admin || is_op)
				x[0] = 'P';
			else
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin),
					Channel_Name(Channel));
			break;
		/* --- Channel user modes --- */
		case 'q': /* Owner */
		case 'a': /* Channel admin */
			if(!is_oper && !is_machine && !is_owner && !is_admin) {
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPPRIVTOOLOW_MSG,
					Client_ID(Origin),
					Channel_Name(Channel));
				goto chan_exit;
			}
		case 'o': /* Channel operator */
			if(!is_oper && !is_machine && !is_owner &&
			   !is_admin && !is_op) {
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin),
					Channel_Name(Channel));
				goto chan_exit;
			}
		case 'h': /* Half Op */
			if(!is_oper && !is_machine && !is_owner &&
			   !is_admin && !is_op) {
				connected = IRC_WriteErrClient(Origin,
					ERR_CHANOPRIVSNEEDED_MSG,
					Client_ID(Origin),
					Channel_Name(Channel));
				goto chan_exit;
			}
		case 'v': /* Voice */
			if (arg_arg > mode_arg) {
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop) {
					client = Client_Search(Req->argv[arg_arg]);
					if (client)
						x[0] = *mode_ptr;
					else
						connected = IRC_WriteErrClient(Origin,
							ERR_NOSUCHNICK_MSG,
							Client_ID(Origin),
							Req->argv[arg_arg]);
				} else {
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				}
				Req->argv[arg_arg][0] = '\0';
				arg_arg++;
			} else {
#ifdef STRICT_RFC
				/* Report an error to the client, when a user
				 * mode should be changed but no nickname is
				 * given. But don't do it when not in "strict"
				 * mode, because most other servers don't do
				 * it as well and some clients send "wired"
				 * MODE commands like "MODE #chan -ooo nick". */
				connected = IRC_WriteErrClient(Origin,
					ERR_NEEDMOREPARAMS_MSG,
					Client_ID(Origin), Req->command);
#endif
				goto chan_exit;
			}
			break;
		/* --- Channel lists --- */
		case 'I': /* Invite lists */
		case 'b': /* Ban lists */
		case 'e': /* Channel exception lists */
			if (Mode_Limit_Reached(Client, mode_arg_count++))
				goto chan_exit;
			if (arg_arg > mode_arg) {
				/* modify list */
				if (is_oper || is_machine || is_owner ||
				    is_admin || is_op || is_halfop) {
					connected = set
					   ? Add_To_List(*mode_ptr, Origin,
						Client, Channel,
						Req->argv[arg_arg])
					   : Del_From_List(*mode_ptr, Origin,
						Client, Channel,
						Req->argv[arg_arg]);
				} else {
					connected = IRC_WriteErrClient(Origin,
						ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(Origin),
						Channel_Name(Channel));
				}
				Req->argv[arg_arg][0] = '\0';
				arg_arg++;
			} else {
				switch (*mode_ptr) {
				case 'I':
					Channel_ShowInvites(Origin, Channel);
					break;
				case 'b':
					Channel_ShowBans(Origin, Channel);
					break;
				case 'e':
					Channel_ShowExcepts(Origin, Channel);
					break;
				}
			}
			break;
		default:
			if (Client_Type(Client) != CLIENT_SERVER) {
				Log(LOG_DEBUG,
				    "Unknown mode \"%c%c\" from \"%s\" on %s!?",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin), Channel_Name(Channel));
				connected = IRC_WriteErrClient(Origin,
					ERR_UNKNOWNMODE_MSG,
					Client_ID(Origin), *mode_ptr,
					Channel_Name(Channel));
				x[0] = '\0';
			} else {
				Log(LOG_DEBUG,
				    "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin), Channel_Name(Channel));
				x[0] = *mode_ptr;
			}
		}

		if (!connected)
			break;

		/* Is there a valid mode change? */
		if (!x[0])
			continue;

		/* Validate target client */
		if (client && (!Channel_IsMemberOf(Channel, client))) {
			if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
						Client_ID(Origin),
						Client_ID(client),
						Channel_Name(Channel)))
				break;
			continue;
		}

		if (client) {
			/* Channel-User-Mode */
			retval = set
			       ? Channel_UserModeAdd(Channel, client, x[0])
			       : Channel_UserModeDel(Channel, client, x[0]);
			if (retval) {
				strlcat(the_args, " ", sizeof(the_args));
				strlcat(the_args, Client_ID(client),
					sizeof(the_args));
				strlcat(the_modes, x, sizeof(the_modes));
				LogDebug
				    ("User \"%s\": Mode change on %s, now \"%s\"",
				     Client_Mask(client), Channel_Name(Channel),
				     Channel_UserModes(Channel, client));
			}
		} else {
			/* Channel-Mode */
			retval = set
			       ? Channel_ModeAdd(Channel, x[0])
			       : Channel_ModeDel(Channel, x[0]);
			if (retval) {
				strlcat(the_modes, x, sizeof(the_modes));
				LogDebug("Channel %s: Mode change, now \"%s\".",
					 Channel_Name(Channel),
					 Channel_Modes(Channel));
			}
		}

		/* Are there additional arguments to add? */
		if (argadd[0]) {
			strlcat(the_args, " ", sizeof(the_args));
			strlcat(the_args, argadd, sizeof(the_args));
		}
	}

      chan_exit:
	/* Are there changed modes? */
	if (the_modes[1]) {
		/* Clean up mode string */
		len = strlen(the_modes) - 1;
		if ((the_modes[len] == '+') || (the_modes[len] == '-'))
			the_modes[len] = '\0';

		if (Client_Type(Client) == CLIENT_SERVER) {
			/* MODE requests for local channels from other servers
			 * are definitely invalid! */
			if (Channel_IsLocal(Channel)) {
				Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
				return CONNECTED;
			}

			/* Forward mode changes to channel users and all the
			 * other remote servers: */
			IRC_WriteStrServersPrefix(Client, Origin,
				"MODE %s %s%s", Channel_Name(Channel),
				the_modes, the_args);
			IRC_WriteStrChannelPrefix(Client, Channel, Origin,
				false, "MODE %s %s%s", Channel_Name(Channel),
				the_modes, the_args);
		} else {
			if (use_servermode)
				Origin = Client_ThisServer();
			/* Send reply to client and inform other servers and channel users */
			connected = IRC_WriteStrClientPrefix(Client, Origin,
					"MODE %s %s%s", Channel_Name(Channel),
					the_modes, the_args);
			/* Only forward requests for non-local channels */
			if (!Channel_IsLocal(Channel))
				IRC_WriteStrServersPrefix(Client, Origin,
					"MODE %s %s%s", Channel_Name(Channel),
					the_modes, the_args);
			IRC_WriteStrChannelPrefix(Client, Channel, Origin,
				false, "MODE %s %s%s", Channel_Name(Channel),
				the_modes, the_args);
		}
	}

	return connected;
} /* Channel_Mode */
Beispiel #7
0
/**
 * Handle client mode requests
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @param Origin The originator of the MODE command (prefix).
 * @param Target The target (client) of this MODE command.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
{
	char the_modes[COMMAND_LEN], x[2], *mode_ptr;
	bool ok, set;
	bool send_RPL_HOSTHIDDEN_MSG = false;
	int mode_arg;
	size_t len;

	/* Is the client allowed to request or change the modes? */
	if (Client_Type(Client) == CLIENT_USER) {
		/* Users are only allowed to manipulate their own modes! */
		if (Target != Client)
			return IRC_WriteErrClient(Client,
						  ERR_USERSDONTMATCH_MSG,
						  Client_ID(Client));
	}

	/* Mode request: let's answer it :-) */
	if (Req->argc == 1)
		return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
					  Client_ID(Target),
					  Client_Modes(Target));

	mode_arg = 1;
	mode_ptr = Req->argv[mode_arg];

	/* Initial state: set or unset modes? */
	if (*mode_ptr == '+') {
		set = true;
		strcpy(the_modes, "+");
	} else if (*mode_ptr == '-') {
		set = false;
		strcpy(the_modes, "-");
	} else
		return IRC_WriteErrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
					  Client_ID(Origin));

	x[1] = '\0';
	ok = CONNECTED;
	while (mode_ptr) {
		mode_ptr++;
		if (!*mode_ptr) {
			/* Try next argument if there's any */
			mode_arg++;
			if (mode_arg < Req->argc)
				mode_ptr = Req->argv[mode_arg];
			else
				break;
		}

		switch(*mode_ptr) {
		  case '+':
		  case '-':
			if ((*mode_ptr == '+' && !set)
			    || (*mode_ptr == '-' && set)) {
				/* Action modifier ("+"/"-") must be changed */
				len = strlen(the_modes) - 1;
				if (the_modes[len] == '+'
				    || the_modes[len] == '-') {
					/* Last character in the "result
					 * string" was an "action", so just
					 * overwrite it with the new action */
					the_modes[len] = *mode_ptr;
				} else {
					/* Append new modifier character to
					 * the resulting mode string */
					x[0] = *mode_ptr;
					strlcat(the_modes, x,
						sizeof(the_modes));
				}
				if (*mode_ptr == '+')
					set = true;
				else
					set = false;
			}
			continue;
		}

		/* Validate modes */
		x[0] = '\0';
		switch (*mode_ptr) {
		case 'b': /* Block private msgs */
		case 'C': /* Only messages from clients sharing a channel */
		case 'i': /* Invisible */
		case 'I': /* Hide channel list from WHOIS */
		case 's': /* Server messages */
		case 'w': /* Wallops messages */
			x[0] = *mode_ptr;
			break;
		case 'a': /* Away */
			if (Client_Type(Client) == CLIENT_SERVER) {
				x[0] = 'a';
				Client_SetAway(Origin, DEFAULT_AWAY_MSG);
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'B': /* Bot */
			if (Client_HasMode(Client, 'r'))
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			else
				x[0] = 'B';
			break;
		case 'c': /* Receive connect notices */
		case 'q': /* KICK-protected user */
		case 'F': /* disable flood protection */
			  /* (only settable by IRC operators!) */
			if (!set || Client_Type(Client) == CLIENT_SERVER
			    || Client_HasMode(Origin, 'o'))
				x[0] = *mode_ptr;
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'o': /* IRC operator (only unsettable!) */
			if (!set || Client_Type(Client) == CLIENT_SERVER) {
				x[0] = 'o';
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'r': /* Restricted (only settable) */
			if (set || Client_Type(Client) == CLIENT_SERVER)
				x[0] = 'r';
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			break;
		case 'R': /* Registered (not [un]settable by clients) */
			if (Client_Type(Client) == CLIENT_SERVER)
				x[0] = 'R';
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_NICKREGISTER_MSG,
							Client_ID(Origin));
			break;
		case 'x': /* Cloak hostname */
			if (Client_HasMode(Client, 'r'))
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			else if (!set || Conf_CloakHostModeX[0]
				 || Client_Type(Client) == CLIENT_SERVER
				 || Client_HasMode(Origin, 'o')) {
				x[0] = 'x';
				send_RPL_HOSTHIDDEN_MSG = true;
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		default:
			if (Client_Type(Client) != CLIENT_SERVER) {
				Log(LOG_DEBUG,
				    "Unknown mode \"%c%c\" from \"%s\"!?",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin));
				ok = IRC_WriteErrClient(Origin,
							ERR_UMODEUNKNOWNFLAG2_MSG,
							Client_ID(Origin),
							set ? '+' : '-',
							*mode_ptr);
				x[0] = '\0';
			} else {
				Log(LOG_DEBUG,
				    "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin), Client_ID(Target));
				x[0] = *mode_ptr;
			}
		}

		if (!ok)
			break;

		/* Is there a valid mode change? */
		if (!x[0])
			continue;

		if (set) {
			if (Client_ModeAdd(Target, x[0]))
				strlcat(the_modes, x, sizeof(the_modes));
		} else {
			if (Client_ModeDel(Target, x[0]))
				strlcat(the_modes, x, sizeof(the_modes));
		}
	}

	/* Are there changed modes? */
	if (the_modes[1]) {
		/* Remove needless action modifier characters */
		len = strlen(the_modes) - 1;
		if (the_modes[len] == '+' || the_modes[len] == '-')
			the_modes[len] = '\0';

		if (Client_Type(Client) == CLIENT_SERVER) {
			/* Forward modes to other servers */
			if (Client_Conn(Target) != NONE) {
				/* Remote server (service?) changed modes
				 * for one of our clients. Inform it! */
				IRC_WriteStrClientPrefix(Target, Origin,
							 "MODE %s :%s",
							 Client_ID(Target),
							 the_modes);
			}
			IRC_WriteStrServersPrefix(Client, Origin,
						  "MODE %s :%s",
						  Client_ID(Target),
						  the_modes);
		} else {
			/* Send reply to client and inform other servers */
			ok = IRC_WriteStrClientPrefix(Client, Origin,
						      "MODE %s :%s",
						      Client_ID(Target),
						      the_modes);
			IRC_WriteStrServersPrefix(Client, Origin,
						  "MODE %s :%s",
						  Client_ID(Target),
						  the_modes);
		}

		if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
			/* A new (cloaked) hostname must be announced */
			IRC_WriteStrClientPrefix(Target, Origin,
						 RPL_HOSTHIDDEN_MSG,
						 Client_ID(Target),
						 Client_HostnameDisplayed(Target));

		}

		LogDebug("%s \"%s\": Mode change, now \"%s\".",
			 Client_TypeText(Target), Client_Mask(Target),
			 Client_Modes(Target));
	}

	return ok;
} /* Client_Mode */
Beispiel #8
0
GLOBAL void
Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool SendQuit )
{
	/* remove a client */
	
	CLIENT *last, *c;
	char msg[COMMAND_LEN];
	const char *txt;

	assert( Client != NULL );

	txt = LogMsg ? LogMsg : FwdMsg;
	if (!txt)
		txt = "Reason unknown";

	/* netsplit message */
	if( Client->type == CLIENT_SERVER ) {
		strlcpy(msg, This_Server->id, sizeof (msg));
		strlcat(msg, " ", sizeof (msg));
		strlcat(msg, Client->id, sizeof (msg));
	}

	last = NULL;
	c = My_Clients;
	while( c )
	{
		if(( Client->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c != Client ))
		{
			/*
			 * The client that is about to be removed is a server,
			 * the client we are checking right now is a child of that
			 * server and thus has to be removed, too.
			 *
			 * Call Client_Destroy() recursively with the server as the
			 * new "object to be removed". This starts the cycle again, until
			 * all servers that are linked via the original server have been
			 * removed.
			 */
			Client_Destroy( c, NULL, msg, false );
			last = NULL;
			c = My_Clients;
			continue;
		}
		if( c == Client )
		{
			/* found  the client: remove it */
			if( last ) last->next = c->next;
			else My_Clients = (CLIENT *)c->next;

			if(c->type == CLIENT_USER || c->type == CLIENT_SERVICE)
				Destroy_UserOrService(c, txt, FwdMsg, SendQuit);
			else if( c->type == CLIENT_SERVER )
			{
				if (c != This_Server) {
					if (c->conn_id != NONE)
						Log(LOG_NOTICE|LOG_snotice,
						    "Server \"%s\" unregistered (connection %d): %s.",
						c->id, c->conn_id, txt);
					else
						Log(LOG_NOTICE|LOG_snotice,
						    "Server \"%s\" unregistered: %s.",
						    c->id, txt);
				}

				/* inform other servers */
				if( ! NGIRCd_SignalQuit )
				{
					if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :%s", c->id, FwdMsg );
					else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :", c->id );
				}
			}
			else
			{
				if (c->conn_id != NONE) {
					if (c->id[0])
						Log(LOG_NOTICE,
						    "Client \"%s\" unregistered (connection %d): %s.",
						    c->id, c->conn_id, txt);
					else
						Log(LOG_NOTICE,
						    "Client unregistered (connection %d): %s.",
						    c->conn_id, txt);
				} else {
					Log(LOG_WARNING,
					    "Unregistered unknown client \"%s\": %s",
					    c->id[0] ? c->id : "(No Nick)", txt);
				}
			}

			Free_Client(&c);
			break;
		}
		last = c;
		c = (CLIENT *)c->next;
	}
} /* Client_Destroy */
Beispiel #9
0
static bool
Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const char *Reason, bool InformServer )
{
	CL2CHAN *cl2chan, *last_cl2chan;
	CHANNEL *c;

	assert( Chan != NULL );
	assert( Client != NULL );
	assert( Origin != NULL );
	assert( Reason != NULL );

	/* Do not inform other servers if the channel is local to this server,
	 * regardless of what the caller requested! */
	if(InformServer)
		InformServer = !Channel_IsLocal(Chan);

	last_cl2chan = NULL;
	cl2chan = My_Cl2Chan;
	while( cl2chan )
	{
		if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
		last_cl2chan = cl2chan;
		cl2chan = cl2chan->next;
	}
	if( ! cl2chan ) return false;

	c = cl2chan->channel;
	assert( c != NULL );

	/* maintain cl2chan list */
	if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
	else My_Cl2Chan = cl2chan->next;
	free( cl2chan );

	switch( Type )
	{
		case REMOVE_QUIT:
			/* QUIT: other servers have already been notified, 
			 * see Client_Destroy(); so only inform other clients
			 * in same channel. */
			assert( InformServer == false );
			LogDebug("User \"%s\" left channel \"%s\" (%s).",
					Client_Mask( Client ), c->name, Reason );
			break;
		case REMOVE_KICK:
			/* User was KICKed: inform other servers (public
			 * channels) and all users in the channel */
			if( InformServer )
				IRC_WriteStrServersPrefix( Client_NextHop( Origin ),
					Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason);
			IRC_WriteStrChannelPrefix(Client, c, Origin, false, "KICK %s %s :%s",
							c->name, Client_ID( Client ), Reason );
			if ((Client_Conn(Client) > NONE) &&
					(Client_Type(Client) == CLIENT_USER))
			{
				IRC_WriteStrClientPrefix(Client, Origin, "KICK %s %s :%s",
								c->name, Client_ID( Client ), Reason);
			}
			LogDebug("User \"%s\" has been kicked off \"%s\" by \"%s\": %s.",
				Client_Mask( Client ), c->name, Client_ID(Origin), Reason);
			break;
		default: /* PART */
			if (Conf_MorePrivacy)
				Reason = "";

			if (InformServer)
				IRC_WriteStrServersPrefix(Origin, Client, "PART %s :%s", c->name, Reason);

			IRC_WriteStrChannelPrefix(Origin, c, Client, false, "PART %s :%s",
									c->name, Reason);

			if ((Client_Conn(Origin) > NONE) &&
					(Client_Type(Origin) == CLIENT_USER))
			{
				IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason);
				LogDebug("User \"%s\" left channel \"%s\" (%s).",
					Client_Mask(Client), c->name, Reason);
			}
	}

	/* When channel is empty and is not pre-defined, delete */
	if( ! Channel_HasMode( Chan, 'P' ))
	{
		if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
	}

	return true;
} /* Remove_Client */
Beispiel #10
0
/**
 * Handler for the IRC command "SERVER".
 * See RFC 2813 section 4.1.2.
 */
GLOBAL bool
IRC_SERVER( CLIENT *Client, REQUEST *Req )
{
	char str[LINE_LEN];
	CLIENT *from, *c;
	int i;
	CONN_ID con;
	
	assert( Client != NULL );
	assert( Req != NULL );

	/* 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);

	if (Client_Type(Client) == CLIENT_GOTPASS ||
	    Client_Type(Client) == CLIENT_GOTPASS_2813) {
		/* We got a PASS command from the peer, and now a SERVER
		 * command: the peer tries to register itself as a server. */
		LogDebug("Connection %d: got SERVER command (new server link) ...",
			Client_Conn(Client));

		if(( Req->argc != 2 ) && ( Req->argc != 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );

		/* Ist this server configured on out side? */
		for( i = 0; i < MAX_SERVERS; i++ ) if( strcasecmp( Req->argv[0], Conf_Server[i].name ) == 0 ) break;
		if( i >= MAX_SERVERS )
		{
			Log( LOG_ERR, "Connection %d: Server \"%s\" not configured here!", Client_Conn( Client ), Req->argv[0] );
			Conn_Close( Client_Conn( Client ), NULL, "Server not configured here", true);
			return DISCONNECTED;
		}
		if( strcmp( Client_Password( Client ), Conf_Server[i].pwd_in ) != 0 )
		{
			/* wrong password */
			Log( LOG_ERR, "Connection %d: Got bad password from server \"%s\"!", Client_Conn( Client ), Req->argv[0] );
			Conn_Close( Client_Conn( Client ), NULL, "Bad password", true);
			return DISCONNECTED;
		}
		
		/* Is there a registered server with this ID? */
		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;

		Client_SetID( Client, Req->argv[0] );
		Client_SetHops( Client, 1 );
		Client_SetInfo( Client, Req->argv[Req->argc - 1] );

		/* Is this server registering on our side, or are we connecting to
		 * a remote server? */
		con = Client_Conn(Client);
		if (Client_Token(Client) != TOKEN_OUTBOUND) {
			/* Incoming connection, send user/pass */
			if (!IRC_WriteStrClient(Client, "PASS %s %s",
						Conf_Server[i].pwd_out,
						NGIRCd_ProtoID)
			    || !IRC_WriteStrClient(Client, "SERVER %s 1 :%s",
						   Conf_ServerName,
						   Conf_ServerInfo)) {
				    Conn_Close(con, "Unexpected server behavior!",
					       NULL, false);
				    return DISCONNECTED;
			}
			Client_SetIntroducer(Client, Client);
			Client_SetToken(Client, 1);
		} else {
			/* outgoing connect, we already sent a SERVER and PASS
			 * command to the peer */
			Client_SetToken(Client, atoi(Req->argv[1]));
		}

		/* Mark this connection as belonging to an configured server */
		Conf_SetServer(i, con);

		/* Check protocol level */
		if (Client_Type(Client) == CLIENT_GOTPASS) {
			/* We got a "simple" PASS command, so the peer is
			 * using the protocol as defined in RFC 1459. */
			if (! (Conn_Options(con) & CONN_RFC1459))
				Log(LOG_INFO,
				    "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.",
				    con, Client_ID(Client));
			Conn_SetOption(con, CONN_RFC1459);
		}

		Client_SetType(Client, CLIENT_UNKNOWNSERVER);

#ifdef ZLIB
		if (strchr(Client_Flags(Client), 'Z') && !Zip_InitConn(con)) {
			Conn_Close( con, "Can't inizialize compression (zlib)!", NULL, false );
			return DISCONNECTED;
		}
#endif

#ifdef IRCPLUS
		if (strchr(Client_Flags(Client), 'H')) {
			LogDebug("Peer supports IRC+ extended server handshake ...");
			if (!IRC_Send_ISUPPORT(Client))
				return DISCONNECTED;
			return IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG,
						  Client_ID(Client));
		} else {
#endif
			if (Conf_MaxNickLength != CLIENT_NICK_LEN_DEFAULT)
				Log(LOG_CRIT,
				    "Attention: this server uses a non-standard nick length, but the peer doesn't support the IRC+ extended server handshake!");
#ifdef IRCPLUS
		}
#endif

		return IRC_Num_ENDOFMOTD(Client, Req);
	}
	else if( Client_Type( Client ) == CLIENT_SERVER )
	{
		/* New server is being introduced to the network */

		if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );

		/* check for existing server with same ID */
		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;

		from = Client_Search( Req->prefix );
		if( ! from )
		{
			/* Uh, Server, that introduced the new server is unknown?! */
			Log( LOG_ALERT, "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)", Req->prefix, Client_Conn( Client ));
			Conn_Close( Client_Conn( Client ), NULL, "Unknown ID in prefix of SERVER", true);
			return DISCONNECTED;
		}

		c = Client_NewRemoteServer(Client, Req->argv[0], from, atoi(Req->argv[1]), atoi(Req->argv[2]), Req->argv[3], true);
		if (!c) {
			Log( LOG_ALERT, "Can't create client structure for server! (on connection %d)", Client_Conn( Client ));
			Conn_Close( Client_Conn( Client ), NULL, "Can't allocate client structure for remote server", true);
			return DISCONNECTED;
		}

		if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) snprintf( str, sizeof( str ), "connected to %s, ", Client_ID( from ));
		else strcpy( str, "" );
		Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" registered (via %s, %s%d hop%s).", Client_ID( c ), Client_ID( Client ), str, Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" );

		/* notify other servers */
		IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ));

		return CONNECTED;
	} else
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);
} /* IRC_SERVER */
Beispiel #11
0
GLOBAL bool
IRC_NJOIN( CLIENT *Client, REQUEST *Req )
{
	char nick_in[COMMAND_LEN], nick_out[COMMAND_LEN], *channame, *ptr, modes[8];
	bool is_op, is_voiced;
	CHANNEL *chan;
	CLIENT *c;
	
	assert( Client != NULL );
	assert( Req != NULL );

	if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );

	strlcpy( nick_in, Req->argv[1], sizeof( nick_in ));
	strcpy( nick_out, "" );

	channame = Req->argv[0];
	ptr = strtok( nick_in, "," );
	while( ptr )
	{
		is_op = is_voiced = false;
		
		/* cut off prefixes */
		while(( *ptr == '@' ) || ( *ptr == '+' ))
		{
			if( *ptr == '@' ) is_op = true;
			if( *ptr == '+' ) is_voiced = true;
			ptr++;
		}

		c = Client_Search( ptr );
		if( c )
		{
			Channel_Join( c, channame );
			chan = Channel_Search( channame );
			assert( chan != NULL );
			
			if( is_op ) Channel_UserModeAdd( chan, c, 'o' );
			if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' );

			/* announce to channel... */
			IRC_WriteStrChannelPrefix( Client, chan, c, false, "JOIN :%s", channame );

			/* set Channel-User-Modes */
			strlcpy( modes, Channel_UserModes( chan, c ), sizeof( modes ));
			if( modes[0] )
			{
				/* send modes to channel */
				IRC_WriteStrChannelPrefix( Client, chan, Client, false, "MODE %s +%s %s", channame, modes, Client_ID( c ));
			}

			if( nick_out[0] != '\0' ) strlcat( nick_out, ",", sizeof( nick_out ));
			if( is_op ) strlcat( nick_out, "@", sizeof( nick_out ));
			if( is_voiced ) strlcat( nick_out, "+", sizeof( nick_out ));
			strlcat( nick_out, ptr, sizeof( nick_out ));
		}
		else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame );
		
		/* search for next Nick */
		ptr = strtok( NULL, "," );
	}

	/* forward to other servers */
	if( nick_out[0] != '\0' ) IRC_WriteStrServersPrefix( Client, Client_ThisServer( ), "NJOIN %s :%s", Req->argv[0], nick_out );

	return CONNECTED;
} /* IRC_NJOIN */
Beispiel #12
0
/**
 * Handler for the IRC "TOPIC" 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_TOPIC( CLIENT *Client, REQUEST *Req )
{
	CHANNEL *chan;
	CLIENT *from;
	char *topic;
	bool r, topic_power;

	assert( Client != NULL );
	assert( Req != NULL );

	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)

	chan = Channel_Search(Req->argv[0]);
	if (!chan)
		return IRC_WriteErrClient(from, ERR_NOSUCHCHANNEL_MSG,
					  Client_ID(from), Req->argv[0]);

	/* Only remote servers and channel members are allowed to change the
	 * channel topic, and IRC operators when the Conf_OperCanMode option
	 * is set in the server configuration. */
	if (Client_Type(Client) != CLIENT_SERVER) {
		topic_power = Client_HasMode(from, 'o');
		if (!Channel_IsMemberOf(chan, from)
		    && !(Conf_OperCanMode && topic_power))
			return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
						  Client_ID(from), Req->argv[0]);
	} else
		topic_power = true;

	if (Req->argc == 1) {
		/* Request actual topic */
		topic = Channel_Topic(chan);
		if (*topic) {
			r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
					       Client_ID(Client),
					       Channel_Name(chan), topic);
#ifndef STRICT_RFC
			if (!r)
				return r;
			r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
					       Client_ID(Client),
					       Channel_Name(chan),
					       Channel_TopicWho(chan),
					       Channel_TopicTime(chan));
#endif
			return r;
		}
		else
			return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
						  Client_ID(from),
						  Channel_Name(chan));
	}

	if (Channel_HasMode(chan, 't')) {
		/* Topic Lock. Is the user a channel op or IRC operator? */
		if(!topic_power &&
		   !Channel_UserHasMode(chan, from, 'h') &&
		   !Channel_UserHasMode(chan, from, 'o') &&
		   !Channel_UserHasMode(chan, from, 'a') &&
		   !Channel_UserHasMode(chan, from, 'q'))
			return IRC_WriteErrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
						  Client_ID(from),
						  Channel_Name(chan));
	}

	/* Set new topic */
	Channel_SetTopic(chan, from, Req->argv[1]);
	LogDebug("%s \"%s\" set topic on \"%s\": %s",
		 Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
		 Req->argv[1][0] ? Req->argv[1] : "<none>");

	if (Conf_OperServerMode)
		from = Client_ThisServer();

	/* Update channel and forward new topic to other servers */
	if (!Channel_IsLocal(chan))
		IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
					  Req->argv[0], Req->argv[1]);
	IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s",
				  Req->argv[0], Req->argv[1]);

	if (Client_Type(Client) == CLIENT_USER)
		return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
						Req->argv[0], Req->argv[1]);
	else
		return CONNECTED;
} /* IRC_TOPIC */
Beispiel #13
0
/**
 * Handler for the IRC "NICK" command.
 *
 * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
 *
 * 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.
 *
 * @param Client	The client from which this command has been received.
 * @param Req		Request structure with prefix and all parameters.
 * @returns		CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_NICK( CLIENT *Client, REQUEST *Req )
{
	CLIENT *intr_c, *target, *c;
	char *nick, *user, *hostname, *modes, *info;
	int token, hops;

	assert( Client != NULL );
	assert( Req != NULL );

	/* Some IRC clients, for example BitchX, send the NICK and USER
	 * commands in the wrong order ... */
	if(Client_Type(Client) == CLIENT_UNKNOWN
	    || Client_Type(Client) == CLIENT_GOTPASS
	    || Client_Type(Client) == CLIENT_GOTNICK
#ifndef STRICT_RFC
	    || Client_Type(Client) == CLIENT_GOTUSER
#endif
	    || Client_Type(Client) == CLIENT_USER
	    || Client_Type(Client) == CLIENT_SERVICE
	    || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
	{
		/* User registration or change of nickname */

		/* Wrong number of arguments? */
		if( Req->argc != 1 )
			return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
						   Client_ID( Client ),
						   Req->command );

		/* Search "target" client */
		if( Client_Type( Client ) == CLIENT_SERVER )
		{
			target = Client_Search( Req->prefix );
			if( ! target )
				return IRC_WriteStrClient( Client,
							   ERR_NOSUCHNICK_MSG,
							   Client_ID( Client ),
							   Req->argv[0] );
		}
		else
		{
			/* Is this a restricted client? */
			if( Client_HasMode( Client, 'r' ))
				return IRC_WriteStrClient( Client,
							   ERR_RESTRICTED_MSG,
							   Client_ID( Client ));

			target = Client;
		}

#ifndef STRICT_RFC
		/* 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;
#endif

		/* Check that the new nickname is available. Special case:
		 * the client only changes from/to upper to lower case. */
		if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
		{
			if( ! Client_CheckNick( target, Req->argv[0] ))
				return CONNECTED;
		}

		if (Client_Type(target) != CLIENT_USER &&
		    Client_Type(target) != CLIENT_SERVICE &&
		    Client_Type(target) != CLIENT_SERVER) {
			/* New client */
			LogDebug("Connection %d: got valid NICK command ...",
			     Client_Conn( Client ));

			/* Register new nickname of this client */
			Client_SetID( target, Req->argv[0] );

#ifndef STRICT_RFC
			if (Conf_AuthPing) {
				Conn_SetAuthPing(Client_Conn(Client), rand());
				IRC_WriteStrClient(Client, "PING :%ld",
					Conn_GetAuthPing(Client_Conn(Client)));
				LogDebug("Connection %d: sent AUTH PING %ld ...",
					Client_Conn(Client),
					Conn_GetAuthPing(Client_Conn(Client)));
			}
#endif

			/* If we received a valid USER command already then
			 * register the new client! */
			if( Client_Type( Client ) == CLIENT_GOTUSER )
				return Login_User( Client );
			else
				Client_SetType( Client, CLIENT_GOTNICK );
		} else {
			/* Nickname change */
			if (Client_Conn(target) > NONE) {
				/* Local client */
				Log(LOG_INFO,
				    "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
				    Client_TypeText(target), Client_Mask(target),
				    Client_Conn(target), Client_ID(target),
				    Req->argv[0]);
				Conn_UpdateIdle(Client_Conn(target));
			} else {
				/* Remote client */
				LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
					 Client_TypeText(target),
					 Client_Mask(target), Client_ID(target),
					 Req->argv[0]);
			}

			/* 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] );

			/* Register old nickname for WHOWAS queries */
			Client_RegisterWhowas( target );

			/* Save new nickname */
			Client_SetID( target, Req->argv[0] );

			IRC_SetPenalty( target, 2 );
		}

		return CONNECTED;
	} 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) {
			/* RFC 2813 compatible syntax */
			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 {
			/* RFC 1459 compatible syntax */
			nick = Req->argv[0];
			hops = 1;
			user = Req->argv[0];
			hostname = Client_ID(Client);
			token = atoi(Req->argv[1]);
			modes = "";
			info = Req->argv[0];
		}

		c = Client_Search(nick);
		if(c) {
			/*
			 * the new nick is already present on this server:
			 * the new and the old one have to be disconnected now.
			 */
			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;
		}

		/* Find the Server this client is connected to */
		intr_c = Client_GetFromToken(Client, token);
		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;
		}

		c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
					 token, modes, info, true);
		if( ! c )
		{
			/* out of memory, need to disconnect client to keep network state consistent */
			Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
			Kill_Nick( Req->argv[0], "Server error" );
			return CONNECTED;
		}

		/* 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! */
		if (Req->argc < 7) {
			LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
				 Client_Mask(c));
			Client_SetType(c, CLIENT_GOTNICK);
		} else
			Client_Introduce(Client, c, CLIENT_USER);

		return CONNECTED;
	}
	else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
} /* IRC_NICK */