Ejemplo n.º 1
0
/*
 * Reply to a channel mode request.
 *
 * @param Origin The originator of the MODE command (prefix).
 * @param Channel The channel of which the modes should be sent.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
{
	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
	const char *mode_ptr;

	if (!Channel_IsMemberOf(Channel, Origin)) {
		/* Not a member: "simple" mode reply */
		if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
					Client_ID(Origin), Channel_Name(Channel),
					Channel_Modes(Channel)))
			return DISCONNECTED;
	} else {
		/* The sender is a member: generate extended reply */
		strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
		mode_ptr = the_modes;
		the_args[0] = '\0';

		while(*mode_ptr) {
			switch(*mode_ptr) {
			case 'l':
				snprintf(argadd, sizeof(argadd), " %lu",
					 Channel_MaxUsers(Channel));
				strlcat(the_args, argadd, sizeof(the_args));
				break;
			case 'k':
				strlcat(the_args, " ", sizeof(the_args));
				strlcat(the_args, Channel_Key(Channel),
					sizeof(the_args));
				break;
			}
			mode_ptr++;
		}
		if (the_args[0])
			strlcat(the_modes, the_args, sizeof(the_modes));

		if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
					Client_ID(Origin), Channel_Name(Channel),
					the_modes))
			return DISCONNECTED;
	}

#ifndef STRICT_RFC
	/* Channel creation time */
	if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
				  Client_ID(Origin), Channel_Name(Channel),
				  Channel_CreationTime(Channel)))
		return DISCONNECTED;
#endif
	return CONNECTED;
}
Ejemplo n.º 2
0
/**
 * Check if a client is allowed to send to a specific channel.
 *
 * @param Chan The channel to check.
 * @param From The client that wants to send.
 * @return true if the client is allowed to send, false otherwise.
 */
static bool
Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
{
	bool is_member, has_voice, is_halfop, is_op, is_chanadmin, is_owner;

	is_member = has_voice = is_halfop = is_op = is_chanadmin = is_owner = false;

	/* The server itself always can send messages :-) */
	if (Client_ThisServer() == From)
		return true;

	if (Channel_IsMemberOf(Chan, From)) {
		is_member = true;
		if (strchr(Channel_UserModes(Chan, From), 'v'))
			has_voice = true;
		if (strchr(Channel_UserModes(Chan, From), 'h'))
			is_halfop = true;
		if (strchr(Channel_UserModes(Chan, From), 'o'))
			is_op = true;
		if (strchr(Channel_UserModes(Chan, From), 'a'))
			is_chanadmin = true;
		if (strchr(Channel_UserModes(Chan, From), 'q'))
			is_owner = true;
	}

	/*
	 * Is the client allowed to write to channel?
	 *
	 * If channel mode n set: non-members cannot send to channel.
	 * If channel mode m set: need voice.
	 */
	if (strchr(Channel_Modes(Chan), 'n') && !is_member)
		return false;

	if (strchr(Channel_Modes(Chan), 'M') && !Client_HasMode(From, 'R')
	    && !Client_HasMode(From, 'o'))
		return false;

	if (has_voice || is_halfop || is_op || is_chanadmin || is_owner)
		return true;

	if (strchr(Channel_Modes(Chan), 'm'))
		return false;

	if (Lists_Check(&Chan->list_excepts, From))
		return true;

	return !Lists_Check(&Chan->list_bans, From);
}
Ejemplo n.º 3
0
/**
 * Get number of channels this server knows and that are "visible" to
 * the given client. If no client is given, all channels will be counted.
 *
 * @param Client The client to check or NULL.
 * @return Number of channels visible to the client.
 */
GLOBAL unsigned long
Channel_CountVisible (CLIENT *Client)
{
	CHANNEL *c;
	unsigned long count = 0;

	c = My_Channels;
	while(c) {
		if (Client) {
			if (!strchr(Channel_Modes(c), 's')
			    || Channel_IsMemberOf(c, Client))
				count++;
		} else
			count++;
		c = c->next;
	}
	return count;
}
Ejemplo n.º 4
0
GLOBAL bool
Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command,
	      bool SendErrors, const char *Text)
{
	if (!Can_Send_To_Channel(Chan, From)) {
		if (! SendErrors)
			return CONNECTED;	/* no error, see RFC 2812 */
		if (strchr(Channel_Modes(Chan), 'M'))
			return IRC_WriteStrClient(From, ERR_NEEDREGGEDNICK_MSG,
						  Client_ID(From), Channel_Name(Chan));
		else
			return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG,
					  Client_ID(From), Channel_Name(Chan));
	}

	if (Client_Conn(From) > NONE)
		Conn_UpdateIdle(Client_Conn(From));

	return IRC_WriteStrChannelPrefix(Client, Chan, From, true,
			"%s %s :%s", Command, Channel_Name(Chan), Text);
}
Ejemplo n.º 5
0
/**
 * Send CHANINFO commands to a new server (inform it about existing channels).
 * @param Client New server
 * @param Chan Channel
 */
static bool
Send_CHANINFO(CLIENT * Client, CHANNEL * Chan)
{
	char *modes, *topic;
	bool has_k, has_l;

#ifdef DEBUG
	Log(LOG_DEBUG, "Sending CHANINFO commands for \"%s\" ...",
	    Channel_Name(Chan));
#endif

	modes = Channel_Modes(Chan);
	topic = Channel_Topic(Chan);

	if (!*modes && !*topic)
		return CONNECTED;

	has_k = Channel_HasMode(Chan, 'k');
	has_l = Channel_HasMode(Chan, 'l');

	/* send CHANINFO */
	if (!has_k && !has_l) {
		if (!*topic) {
			/* "CHANINFO <chan> +<modes>" */
			return IRC_WriteStrClient(Client, "CHANINFO %s +%s",
						  Channel_Name(Chan), modes);
		}
		/* "CHANINFO <chan> +<modes> :<topic>" */
		return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s",
					  Channel_Name(Chan), modes, topic);
	}
	/* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */
	return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s",
				  Channel_Name(Chan), modes,
				  has_k ? Channel_Key(Chan) : "*",
				  has_l ? Channel_MaxUsers(Chan) : 0, topic);
} /* Send_CHANINFO */
Ejemplo n.º 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 */
Ejemplo n.º 7
0
/**
 * Handler for the IRC command "INVITE".
 *
 * @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_INVITE(CLIENT *Client, REQUEST *Req)
{
	CHANNEL *chan;
	CLIENT *target, *from;
	const char *colon_if_necessary;
	bool remember = false;

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

	_IRC_ARGC_EQ_OR_RETURN_(Client, Req, 2)
	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)

	/* Search user */
	target = Client_Search(Req->argv[0]);
	if (!target || (Client_Type(target) != CLIENT_USER))
		return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
				Client_ID(Client), Req->argv[0]);

	chan = Channel_Search(Req->argv[1]);
	if (chan) {
		/* Channel exists. Is the user a valid member of the channel? */
		if (!Channel_IsMemberOf(chan, from))
			return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Req->argv[1]);

		/* Is the channel "invite-disallow"? */
		if (strchr(Channel_Modes(chan), 'V'))
			return IRC_WriteStrClient(from, ERR_NOINVITE_MSG,
				Client_ID(from), Channel_Name(chan));

		/* Is the channel "invite-only"? */
		if (strchr(Channel_Modes(chan), 'i')) {
			/* Yes. The user must be channel owner/admin/operator/halfop! */
			if (!strchr(Channel_UserModes(chan, from), 'q') &&
			    !strchr(Channel_UserModes(chan, from), 'a') &&
			    !strchr(Channel_UserModes(chan, from), 'o') &&
			    !strchr(Channel_UserModes(chan, from), 'h'))
				return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(from), Channel_Name(chan));
			remember = true;
		}

		/* Is the target user already member of the channel? */
		if (Channel_IsMemberOf(chan, target))
			return IRC_WriteStrClient(from, ERR_USERONCHANNEL_MSG,
					Client_ID(from), Req->argv[0], Req->argv[1]);

		/* If the target user is banned on that channel: remember invite */
		if (Lists_Check(Channel_GetListBans(chan), target))
			remember = true;

		if (remember) {
			/* We must remember this invite */
			if (!Channel_AddInvite(chan, Client_Mask(target), true))
				return CONNECTED;
		}
	}

	LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from),
		 Req->argv[0], Req->argv[1]);

	/*
	 * RFC 2812 says:
	 * 'There is no requirement that the channel [..] must exist or be a valid channel'
	 * The problem with this is that this allows the "channel" to contain spaces,
	 * in which case we must prefix its name with a colon to make it clear that
	 * it is only a single argument.
	 */
	colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":"";
	/* Inform target client */
	IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0],
					colon_if_necessary, Req->argv[1]);

	if (Client_Conn(target) > NONE) {
		/* The target user is local, so we have to send the status code */
		if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG,
			Client_ID(from), Req->argv[0], colon_if_necessary, Req->argv[1]))
			return DISCONNECTED;

		if (strchr(Client_Modes(target), 'a') &&
			!IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from),
					Client_ID(target), Client_Away(target)))
				return DISCONNECTED;
	}
	return CONNECTED;
} /* IRC_INVITE */
Ejemplo n.º 8
0
/**
 * Kick user from Channel
 */
GLOBAL void
Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
	     const char *Reason )
{
	CHANNEL *chan;
	char *ptr, *target_modes;
	bool can_kick = false;

	assert(Peer != NULL);
	assert(Target != NULL);
	assert(Origin != NULL);
	assert(Name != NULL);
	assert(Reason != NULL);

	/* Check that channel exists */
	chan = Channel_Search( Name );
	if( ! chan )
	{
		IRC_WriteStrClient( Origin, ERR_NOSUCHCHANNEL_MSG, Client_ID( Origin ), Name );
		return;
	}

	if (Client_Type(Peer) != CLIENT_SERVER &&
	    Client_Type(Origin) != CLIENT_SERVICE) {
		/* Check that user is on the specified channel */
		if (!Channel_IsMemberOf(chan, Origin)) {
			IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG,
					    Client_ID(Origin), Name);
			return;
		}
	}

	if(Client_Type(Peer) == CLIENT_USER) {
		/* Channel mode 'Q' and user mode 'q' on target: nobody but
		 * IRC Operators and servers can kick the target user */
		if ((strchr(Channel_Modes(chan), 'Q')
		     || Client_HasMode(Target, 'q')
		     || Client_Type(Target) == CLIENT_SERVICE)
		    && !Client_HasMode(Origin, 'o')) {
			IRC_WriteStrClient(Origin, ERR_KICKDENY_MSG,
					   Client_ID(Origin), Name,
					   Client_ID(Target));
			return;
		}

		/* Check if client has the rights to kick target */
		ptr = Channel_UserModes(chan, Peer);
		target_modes = Channel_UserModes(chan, Target);
		while(*ptr) {
			/* Owner can kick everyone */
			if ( *ptr == 'q') {
				can_kick = true;
				break;
			}
			/* Admin can't kick owner */
			if ( *ptr == 'a' ) {
				if (!strchr(target_modes, 'q')) {
					can_kick = true;
					break;
				}
			}
			/* Op can't kick owner | admin */
			if ( *ptr == 'o' ) {
				if (!strchr(target_modes, 'q') &&
				    !strchr(target_modes, 'a')) {
					can_kick = true;
					break;
				}
			}
			/* Half Op can't kick owner | admin | op */ 
			if ( *ptr == 'h' ) {
				if (!strchr(target_modes, 'q') &&
				    !strchr(target_modes, 'a') &&
				    !strchr(target_modes, 'o')) {
					can_kick = true;
					break;
				}
			}
			ptr++;
		}

		if(!can_kick) {
			IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG,
				Client_ID(Origin), Name);
			return;
		}
	}

	/* Check that the client to be kicked is on the specified channel */
	if (!Channel_IsMemberOf(chan, Target)) {
		IRC_WriteStrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
				   Client_ID(Origin), Client_ID(Target), Name );
		return;
	}

	/* Kick Client from channel */
	Remove_Client( REMOVE_KICK, chan, Target, Origin, Reason, true);
} /* Channel_Kick */
Ejemplo n.º 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( ! strchr( Channel_Modes( Chan ), 'P' ))
	{
		if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
	}

	return true;
} /* Remove_Client */
Ejemplo n.º 10
0
/**
 * Handler for the IRC+ "CHANINFO" 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_CHANINFO( CLIENT *Client, REQUEST *Req )
{
	char modes_add[COMMAND_LEN], l[16];
	CLIENT *from;
	CHANNEL *chan;
	int arg_topic;

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

	/* Bad number of parameters? */
	if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5)
		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	/* Compatibility kludge */
	if (Req->argc == 5)
		arg_topic = 4;
	else if(Req->argc == 3)
		arg_topic = 2;
	else
		arg_topic = -1;

	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)

	/* Search or create channel */
	chan = Channel_Search( Req->argv[0] );
	if (!chan)
		chan = Channel_Create( Req->argv[0] );
	if (!chan)
		return CONNECTED;

	if (Req->argv[1][0] == '+') {
		if (!*Channel_Modes(chan)) {
			/* OK, this channel doesn't have modes yet,
			 * set the received ones: */
			Channel_SetModes(chan, &Req->argv[1][1]);

			if(Req->argc == 5) {
				if(Channel_HasMode(chan, 'k'))
					Channel_SetKey(chan, Req->argv[2]);
				if(Channel_HasMode(chan, 'l'))
					Channel_SetMaxUsers(chan, atol(Req->argv[3]));
			} else {
				/* Delete modes which we never want to inherit */
				Channel_ModeDel(chan, 'l');
				Channel_ModeDel(chan, 'k');
			}

			strcpy(modes_add, "");
			if (Channel_HasMode(chan, 'l'))  {
				snprintf(l, sizeof(l), " %lu",
					 Channel_MaxUsers(chan));
				strlcat(modes_add, l, sizeof(modes_add));
			}
			if (Channel_HasMode(chan, 'k'))  {
				strlcat(modes_add, " ", sizeof(modes_add));
				strlcat(modes_add, Channel_Key(chan),
					sizeof(modes_add));
			}

			/* Inform members of this channel */
			IRC_WriteStrChannelPrefix(Client, chan, from, false,
						  "MODE %s +%s%s", Req->argv[0],
						  Channel_Modes(chan), modes_add);
		}
	}
	else
		Log(LOG_WARNING, "CHANINFO: invalid MODE format ignored!");

	if (arg_topic > 0) {
		/* We got a topic */
		if (!*Channel_Topic(chan) && Req->argv[arg_topic][0]) {
			/* OK, there is no topic jet */
			Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
			IRC_WriteStrChannelPrefix(Client, chan, from, false,
			     "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
		}
	}

	/* Forward CHANINFO to other servers */
	if (Req->argc == 5)
		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
					      "CHANINFO %s %s %s %s :%s",
					      Req->argv[0], Req->argv[1],
					      Req->argv[2], Req->argv[3],
					      Req->argv[4]);
	else if (Req->argc == 3)
		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
					      "CHANINFO %s %s :%s",
					      Req->argv[0], Req->argv[1],
					      Req->argv[2]);
	else
		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
					      "CHANINFO %s %s",
					      Req->argv[0], Req->argv[1]);

	return CONNECTED;
} /* IRC_CHANINFO */