unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

topic.c (8922B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/topic.c
      3  *   (C) 2004-present The UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers.
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 CMD_FUNC(cmd_topic);
     26 
     27 #define MSG_TOPIC 	"TOPIC"
     28 
     29 ModuleHeader MOD_HEADER
     30   = {
     31 	"topic",
     32 	"5.0",
     33 	"command /topic", 
     34 	"UnrealIRCd Team",
     35 	"unrealircd-6",
     36     };
     37 
     38 /* Forward declarations */
     39 void _set_channel_topic(Client *client, Channel *channel, MessageTag *recv_mtags, const char *topic, const char *set_by, time_t set_at);
     40 
     41 MOD_TEST()
     42 {
     43 	MARK_AS_OFFICIAL_MODULE(modinfo);
     44 	EfunctionAddVoid(modinfo->handle, EFUNC_SET_CHANNEL_TOPIC, _set_channel_topic);
     45 	return MOD_SUCCESS;
     46 }
     47 
     48 MOD_INIT()
     49 {
     50 	CommandAdd(modinfo->handle, MSG_TOPIC, cmd_topic, 4, CMD_USER|CMD_SERVER);
     51 	MARK_AS_OFFICIAL_MODULE(modinfo);
     52 	return MOD_SUCCESS;
     53 }
     54 
     55 MOD_LOAD()
     56 {
     57 	return MOD_SUCCESS;
     58 }
     59 
     60 MOD_UNLOAD()
     61 {
     62 	return MOD_SUCCESS;
     63 }
     64 
     65 void topic_operoverride_msg(Client *client, Channel *channel, const char *topic)
     66 {
     67 	unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_TOPIC", client,
     68 		   "OperOverride: $client.details changed the topic of $channel to '$topic'",
     69 		   log_data_string("override_type", "topic"),
     70 		   log_data_string("topic", topic),
     71 		   log_data_channel("channel", channel));
     72 }
     73 
     74 /** Query or change the channel topic.
     75  *
     76  * Syntax for clients:
     77  * parv[1] = channel
     78  * parv[2] = new topic
     79  *
     80  * Syntax for server to server traffic:
     81  * parv[1] = channel name
     82  * parv[2] = topic nickname
     83  * parv[3] = topic time
     84  * parv[4] = topic text
     85  */
     86 CMD_FUNC(cmd_topic)
     87 {
     88 	Channel *channel = NULL;
     89 	const char *topic = NULL;
     90 	const char *name, *tnick = client->name;
     91 	const char *errmsg = NULL;
     92 	time_t ttime = 0;
     93 	int i = 0;
     94 	Hook *h;
     95 	MessageTag *mtags = NULL;
     96 
     97 	if ((parc < 2) || BadPtr(parv[1]))
     98 	{
     99 		sendnumeric(client, ERR_NEEDMOREPARAMS, "TOPIC");
    100 		return;
    101 	}
    102 
    103 	name = parv[1];
    104 
    105 	channel = find_channel(parv[1]);
    106 	if (!channel)
    107 	{
    108 		sendnumeric(client, ERR_NOSUCHCHANNEL, name);
    109 		return;
    110 	}
    111 
    112 	if (parc > 2 || SecretChannel(channel))
    113 	{
    114 		if (!IsMember(client, channel) && !IsServer(client)
    115 		    && !ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL) && !IsULine(client))
    116 		{
    117 			sendnumeric(client, ERR_NOTONCHANNEL, name);
    118 			return;
    119 		}
    120 		if (parc > 2)
    121 			topic = parv[2];
    122 	}
    123 
    124 	if (parc > 4)
    125 	{
    126 		if (MyUser(client))
    127 		{
    128 			sendnumeric(client, ERR_CANNOTDOCOMMAND, "TOPIC", "Invalid parameters. Usage is TOPIC #channel :topic here");
    129 			return;
    130 		}
    131 		tnick = parv[2];
    132 		ttime = atol(parv[3]);
    133 		topic = parv[4];
    134 	}
    135 
    136 	/* Only asking for the topic */
    137 	if (!topic)
    138 	{
    139 		if (IsServer(client))
    140 			return; /* Servers must maintain state, not ask */
    141 
    142 		for (h = Hooks[HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL]; h; h = h->next)
    143 		{
    144 			i = (*(h->func.intfunc))(client,channel);
    145 			if (i != HOOK_CONTINUE)
    146 				break;
    147 		}
    148 
    149 		/* If you're not a member, and you can't view outside channel, deny */
    150 		if ((!IsMember(client, channel) && i == HOOK_DENY) ||
    151 		    (is_banned(client,channel,BANCHK_JOIN,NULL,NULL) &&
    152 		     !ValidatePermissionsForPath("channel:see:topic",client,NULL,channel,NULL)))
    153 		{
    154 			sendnumeric(client, ERR_NOTONCHANNEL, name);
    155 			return;
    156 		}
    157 
    158 		if (!channel->topic)
    159 			sendnumeric(client, RPL_NOTOPIC, channel->name);
    160 		else
    161 		{
    162 			sendnumeric(client, RPL_TOPIC, channel->name, channel->topic);
    163 			sendnumeric(client, RPL_TOPICWHOTIME, channel->name,
    164 			            channel->topic_nick, (long long)channel->topic_time);
    165 		}
    166 		return;
    167 	}
    168 
    169 	if (ttime && topic && (IsServer(client) || IsULine(client)))
    170 	{
    171 		if (!channel->topic_time || ttime > channel->topic_time || IsULine(client))
    172 		/* The IsUline is to allow services to use an old TS. Apparently
    173 		 * some services do this in their topic enforcement -- codemastr 
    174 		 */
    175 		{
    176 			/* Set the topic */
    177 			safe_strldup(channel->topic, topic, iConf.topic_length+1);
    178 			safe_strldup(channel->topic_nick, tnick, NICKLEN+USERLEN+HOSTLEN+5);
    179 			channel->topic_time = ttime;
    180 
    181 			new_message(client, recv_mtags, &mtags);
    182 			RunHook(HOOKTYPE_TOPIC, client, channel, mtags, topic);
    183 			sendto_server(client, 0, 0, mtags, ":%s TOPIC %s %s %lld :%s",
    184 			    client->id, channel->name, channel->topic_nick,
    185 			    (long long)channel->topic_time, channel->topic);
    186 			sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags,
    187 				       ":%s TOPIC %s :%s",
    188 				       client->name, channel->name, channel->topic);
    189 			free_message_tags(mtags);
    190 		}
    191 		return;
    192 	}
    193 
    194 	/* Topic change. Either locally (check permissions!) or remote, check permissions: */
    195 	if (IsUser(client))
    196 	{
    197 		const char *newtopic = NULL;
    198 		const char *errmsg = NULL;
    199 		int ret = EX_ALLOW;
    200 		int operoverride = 0;
    201 
    202 		for (h = Hooks[HOOKTYPE_CAN_SET_TOPIC]; h; h = h->next)
    203 		{
    204 			int n = (*(h->func.intfunc))(client, channel, topic, &errmsg);
    205 
    206 			if (n == EX_DENY)
    207 			{
    208 				ret = n;
    209 			} else
    210 			if (n == EX_ALWAYS_DENY)
    211 			{
    212 				ret = n;
    213 				break;
    214 			}
    215 		}
    216 
    217 		if (ret == EX_ALWAYS_DENY)
    218 		{
    219 			if (MyUser(client) && errmsg)
    220 				sendto_one(client, NULL, "%s", errmsg); /* send error, if any */
    221 
    222 			if (MyUser(client))
    223 				return; /* reject the topic set (note: we never block remote sets) */
    224 		}
    225 
    226 		if (ret == EX_DENY)
    227 		{
    228 			if (MyUser(client) && !ValidatePermissionsForPath("channel:override:topic", client, NULL, channel, NULL))
    229 			{
    230 				if (errmsg)
    231 					sendto_one(client, NULL, "%s", errmsg);
    232 				return; /* reject */
    233 			} else {
    234 				operoverride = 1; /* allow */
    235 			}
    236 		}
    237 
    238 		/* banned? */
    239 		newtopic = topic;
    240 		if (!check_channel_access(client, channel, "hoaq") && is_banned(client, channel, BANCHK_MSG, &newtopic, &errmsg))
    241 		{
    242 			char buf[512];
    243 
    244 			if (MyUser(client) && !ValidatePermissionsForPath("channel:override:topic", client, NULL, channel, NULL))
    245 			{
    246 				ircsnprintf(buf, sizeof(buf), "You cannot change the topic on %s while being banned", channel->name);
    247 				sendnumeric(client, ERR_CANNOTDOCOMMAND, "TOPIC",  buf);
    248 				return;
    249 			}
    250 			operoverride = 1;
    251 		}
    252 
    253 		if (MyUser(client) && newtopic)
    254 			topic = newtopic; /* process is_banned() changes of topic (eg: text replacement), but only for local clients */
    255 
    256 		if (operoverride)
    257 			topic_operoverride_msg(client, channel, topic);
    258 
    259 		/* For local users, run spamfilters and hooks.. */
    260 		if (MyUser(client))
    261 		{
    262 			Hook *tmphook;
    263 			int n;
    264 
    265 			if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->name, 0, NULL))
    266 				return;
    267 
    268 			for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) {
    269 				topic = (*(tmphook->func.stringfunc))(client, channel, topic);
    270 				if (!topic)
    271 					return;
    272 			}
    273 		}
    274 
    275 		/* At this point 'tnick' is set to client->name.
    276 		 * If set::topic-setter nick-user-host; is set
    277 		 * then we update it here to nick!user@host.
    278 		 */
    279 		if (iConf.topic_setter == SETTER_NICK_USER_HOST)
    280 			tnick = make_nick_user_host(client->name, client->user->username, GetHost(client));
    281 	}
    282 
    283 	_set_channel_topic(client, channel, recv_mtags, topic, tnick, ttime);
    284 }
    285 
    286 /** Set topic on a channel.
    287  * @param client	The client setting the topic
    288  * @param channel	The channel
    289  * @param recv_mtags	Message tags
    290  * @param topic		The new topic (TODO: this function does not support unsetting yet)
    291  * @param set_by	Who set the topic (can be NULL, means client->name)
    292  * @param set_at	When the topic was set (can be 0, means now)
    293  */
    294 void _set_channel_topic(Client *client, Channel *channel, MessageTag *recv_mtags, const char *topic, const char *set_by, time_t set_at)
    295 {
    296 	MessageTag *mtags = NULL;
    297 
    298 	/* Set default values when needed */
    299 	if (set_by == NULL)
    300 		set_by = client->name;
    301 	if (set_at == 0)
    302 		set_at = TStime();
    303 
    304 	/* Set the topic */
    305 	safe_strldup(channel->topic, topic, iConf.topic_length+1);
    306 	safe_strldup(channel->topic_nick, set_by, NICKLEN+USERLEN+HOSTLEN+5);
    307 	channel->topic_time = set_at;
    308 
    309 	/* And broadcast the change - locally and remote */
    310 	new_message(client, recv_mtags, &mtags);
    311 	RunHook(HOOKTYPE_TOPIC, client, channel, mtags, topic);
    312 	sendto_server(client, 0, 0, mtags, ":%s TOPIC %s %s %lld :%s",
    313 	    client->id, channel->name, channel->topic_nick,
    314 	    (long long)channel->topic_time, channel->topic);
    315 	sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s TOPIC %s :%s", client->name, channel->name, channel->topic);
    316 	free_message_tags(mtags);
    317 }