diff --git a/include/client.h b/include/client.h index 4f07cb17..7d65da0c 100644 --- a/include/client.h +++ b/include/client.h @@ -90,7 +90,7 @@ typedef unsigned long flagpage_t; #define FlagClr(set,flag) ((set)->bits[FLAGSET_INDEX(flag)] &= ~FLAGSET_MASK(flag)) /** String containing valid user modes, in no particular order. */ -#define infousermodes "diOoswkgx" +#define infousermodes "diOoswkgxc" /** Operator privileges. */ enum Priv @@ -166,6 +166,7 @@ enum Flag FLAG_DEBUG, /**< send global debug/anti-hack info */ FLAG_ACCOUNT, /**< account name has been set */ FLAG_HIDDENHOST, /**< user's host is hidden */ + FLAG_COMMONCHANS, /**< only accepts messages from users in common channels */ FLAG_LAST_FLAG, /**< number of flags */ FLAG_LOCAL_UMODES = FLAG_LOCOP, /**< First local mode flag */ FLAG_GLOBAL_UMODES = FLAG_OPER /**< First global mode flag */ @@ -599,6 +600,8 @@ struct Client { #define IsHiddenHost(x) HasFlag(x, FLAG_HIDDENHOST) /** Return non-zero if the client has an active PING request. */ #define IsPingSent(x) HasFlag(x, FLAG_PINGSENT) +/** Return non-zero if the client has mode +c (only messages from common channels). */ +#define IsCommonChans(x) HasFlag(x, FLAG_COMMONCHANS) /** Return non-zero if the client has operator or server privileges. */ #define IsPrivileged(x) (IsAnOper(x) || IsServer(x)) @@ -645,6 +648,8 @@ struct Client { #define SetHiddenHost(x) SetFlag(x, FLAG_HIDDENHOST) /** Mark a client as having a pending PING. */ #define SetPingSent(x) SetFlag(x, FLAG_PINGSENT) +/** Mark a client as having mode +c (only messages from those in common channels). */ +#define SetCommonChans(x) SetFlag(x, FLAG_COMMONCHANS) /** Return non-zero if \a sptr sees \a acptr as an operator. */ #define SeeOper(sptr,acptr) (IsAnOper(acptr) && (HasPriv(acptr, PRIV_DISPLAY) \ @@ -680,6 +685,8 @@ struct Client { #define ClearPingSent(x) ClrFlag(x, FLAG_PINGSENT) /** Clear the client's HUB flag. */ #define ClearHub(x) ClrFlag(x, FLAG_HUB) +/** Remove mode +c (only accepts messages from common channels) from the client. */ +#define ClearCommonChans(x) ClrFlag(x, FLAG_COMMONCHANS) /* free flags */ #define FREEFLAG_SOCKET 0x0001 /**< socket needs to be freed */ diff --git a/ircd/ircd_relay.c b/ircd/ircd_relay.c index e39e7dba..a87cd8cb 100644 --- a/ircd/ircd_relay.c +++ b/ircd/ircd_relay.c @@ -400,6 +400,28 @@ void relay_directed_notice(struct Client* sptr, char* name, char* server, const } } +/** Check if two users share a common channel. + * @param[in] sptr Source client. + * @param[in] acptr Target client. + * @return Non-zero if users share a channel, zero otherwise. + */ +static int has_common_channel(struct Client *sptr, struct Client *acptr) +{ + struct Membership *schan, *achan; + + for (schan = cli_user(sptr)->channel; schan; schan = schan->next_channel) { + if (IsZombie(schan) || IsDelayedJoin(schan)) + continue; + for (achan = cli_user(acptr)->channel; achan; achan = achan->next_channel) { + if (IsZombie(achan) || IsDelayedJoin(achan)) + continue; + if (schan->channel == achan->channel) + return 1; /* Found common channel */ + } + } + return 0; /* No common channels */ +} + /** Relay a private message from a local user. * Returns an error if the user does not exist or sending to him would * exceed the source's free targets. Sends an AWAY status message if @@ -426,6 +448,12 @@ void relay_private_message(struct Client* sptr, const char* name, const char* te is_silenced(sptr, acptr)) return; + /* Check +c mode: block private messages from users not in common channels */ + if (IsCommonChans(acptr) && !IsChannelService(sptr) && !IsOper(sptr)) { + if (!has_common_channel(sptr, acptr)) + return; + } + /* * send away message if user away */ @@ -464,6 +492,13 @@ void relay_private_notice(struct Client* sptr, const char* name, const char* tex check_target_limit(sptr, acptr, NULL)) || is_silenced(sptr, acptr)) return; + + /* Check +c mode: block notices from users not in common channels */ + if (IsCommonChans(acptr) && !IsChannelService(sptr) && !IsOper(sptr)) { + if (!has_common_channel(sptr, acptr)) + return; + } + /* * deliver the message */ diff --git a/ircd/s_user.c b/ircd/s_user.c index 6a60e78f..0aca5df2 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -499,7 +499,8 @@ static const struct UserMode { { FLAG_CHSERV, 'k' }, { FLAG_DEBUG, 'g' }, { FLAG_ACCOUNT, 'r' }, - { FLAG_HIDDENHOST, 'x' } + { FLAG_HIDDENHOST, 'x' }, + { FLAG_COMMONCHANS, 'c' } }; /** Length of #userModeList. */ @@ -1081,12 +1082,19 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, } /* There is no -r */ break; + case 'c': + if (what == MODE_ADD) + SetCommonChans(sptr); + else + ClearCommonChans(sptr); + break; default: send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m); break; } } } + /* * Evaluate rules for new user mode * Stop users making themselves operators too easily: