示例#1
0
文件: channel.c 项目: Flupsy/ngircd
/**
 * 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 (Channel_UserHasMode(Chan, From, 'v'))
			has_voice = true;
		if (Channel_UserHasMode(Chan, From, 'h'))
			is_halfop = true;
		if (Channel_UserHasMode(Chan, From, 'o'))
			is_op = true;
		if (Channel_UserHasMode(Chan, From, 'a'))
			is_chanadmin = true;
		if (Channel_UserHasMode(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 (Channel_HasMode(Chan, 'n') && !is_member)
		return false;

	if (Channel_HasMode(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 (Channel_HasMode(Chan, 'm'))
		return false;

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

	return !Lists_Check(&Chan->list_bans, From);
}
示例#2
0
文件: irc-op.c 项目: cofyc/ngircd
/**
 * 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 */
示例#3
0
/**
 * Check weather a local client is allowed to join an already existing
 * channel or not.
 *
 * @param Client	Client that sent the JOIN command
 * @param chan		Channel to check
 * @param channame	Name of the channel
 * @param key		Provided channel key (or NULL)
 * @returns		true if client is allowed to join, false otherwise
 */
static bool
join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
	     const char *key)
{
	bool is_invited, is_banned, is_exception;

	/* Allow IRC operators to overwrite channel limits */
	if (Client_HasMode(Client, 'o'))
		return true;

	is_banned = Lists_Check(Channel_GetListBans(chan), Client);
	is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
	is_invited = Lists_Check(Channel_GetListInvites(chan), Client);

	if (is_banned && !is_invited && !is_exception) {
		/* Client is banned from channel (and not on invite list) */
		IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'i') && !is_invited) {
		/* Channel is "invite-only" and client is not on invite list */
		IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (!Channel_CheckKey(chan, Client, key ? key : "")) {
		/* Channel is protected by a channel key and the client
		 * didn't specify the correct one */
		IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'l') &&
	    (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
		/* There are more clints joined to this channel than allowed */
		IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
		/* Only "secure" clients are allowed, but clients doesn't
		 * use SSL encryption */
		IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
		/* Only IRC operators are allowed! */
		IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
		/* Only registered users are allowed! */
		IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	return true;
} /* join_allowed */
示例#4
0
/**
 * Handler for the IRC "JOIN" 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_JOIN( CLIENT *Client, REQUEST *Req )
{
	char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL;
	CLIENT *target;
	CHANNEL *chan;

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

	_IRC_GET_SENDER_OR_RETURN_(target, Req, Client)

	/* Is argument "0"? */
	if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
		return part_from_all_channels(Client, target);

	/* Are channel keys given? */
	if (Req->argc > 1)
		key = strtok_r(Req->argv[1], ",", &lastkey);

	channame = Req->argv[0];
	channame = strtok_r(channame, ",", &lastchan);

	/* Make sure that "channame" is not the empty string ("JOIN :") */
	if (!channame)
		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	while (channame) {
		flags = NULL;

		/* Did the server include channel-user-modes? */
		if (Client_Type(Client) == CLIENT_SERVER) {
			flags = strchr(channame, 0x7);
			if (flags) {
				*flags = '\0';
				flags++;
			}
		}

		chan = Channel_Search(channame);

		/* Local client? */
		if (Client_Type(Client) == CLIENT_USER) {
			if (chan) {
				/* Already existing channel: already member? */
				if (Channel_IsMemberOf(chan, Client))
				    goto join_next;
			} else {
				/* Channel must be created */
				if (!strchr(Conf_AllowedChannelTypes, channame[0])) {
					/* ... but channel type is not allowed! */
					IRC_WriteErrClient(Client,
						ERR_NOSUCHCHANNEL_MSG,
						Client_ID(Client), channame);
					goto join_next;
				}
			}

			/* Test if the user has reached the channel limit */
			if ((Conf_MaxJoins > 0) &&
			    (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
				if (!IRC_WriteErrClient(Client,
						ERR_TOOMANYCHANNELS_MSG,
						Client_ID(Client), channame))
					return DISCONNECTED;
				goto join_next;
			}

			if (chan) {
				/* Already existing channel: check if the
				 * client is allowed to join */
				if (!join_allowed(Client, chan, channame, key))
					goto join_next;
			} else {
				/* New channel: first user will become channel
				 * operator unless this is a modeless channel */
				if (*channame != '+')
					flags = "o";
			}

			/* Local client: update idle time */
			Conn_UpdateIdle(Client_Conn(Client));
		} else {
			/* Remote server: we don't need to know whether the
			 * client is invited or not, but we have to make sure
			 * that the "one shot" entries (generated by INVITE
			 * commands) in this list become deleted when a user
			 * joins a channel this way. */
			if (chan)
				(void)Lists_Check(Channel_GetListInvites(chan),
						  target);
		}

		/* Join channel (and create channel if it doesn't exist) */
		if (!Channel_Join(target, channame))
			goto join_next;

		if (!chan) { /* channel is new; it has been created above */
			chan = Channel_Search(channame);
			assert(chan != NULL);
			if (Channel_IsModeless(chan)) {
				Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
				Channel_ModeAdd(chan, 'n'); /* no external msgs */
			}
		}
		assert(chan != NULL);

		join_set_channelmodes(chan, target, flags);

		join_forward(Client, target, chan, channame);

		if (!join_send_topic(Client, target, chan, channame))
			break; /* write error */

	join_next:
		/* next channel? */
		channame = strtok_r(NULL, ",", &lastchan);
		if (channame && key)
			key = strtok_r(NULL, ",", &lastkey);
	}
	return CONNECTED;
} /* IRC_JOIN */