unrealircd

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

censor.c (7865B)

      1 /*
      2  * Channel Mode +G
      3  * (C) Copyright 2005-current Bram Matthys and The UnrealIRCd team.
      4  */
      5 
      6 #include "unrealircd.h"
      7 
      8 
      9 ModuleHeader MOD_HEADER
     10   = {
     11 	"chanmodes/censor",
     12 	"4.2",
     13 	"Channel Mode +G",
     14 	"UnrealIRCd Team",
     15 	"unrealircd-6",
     16     };
     17 
     18 
     19 Cmode_t EXTMODE_CENSOR = 0L;
     20 
     21 #define IsCensored(x) ((x)->mode.mode & EXTMODE_CENSOR)
     22 
     23 int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype);
     24 const char *censor_pre_local_part(Client *client, Channel *channel, const char *text);
     25 const char *censor_pre_local_quit(Client *client, const char *text);
     26 int censor_stats_badwords_channel(Client *client, const char *para);
     27 int censor_config_test(ConfigFile *, ConfigEntry *, int, int *);
     28 int censor_config_run(ConfigFile *, ConfigEntry *, int);
     29 
     30 ModuleInfo *ModInfo = NULL;
     31 
     32 ConfigItem_badword *conf_badword_channel = NULL;
     33 
     34 
     35 MOD_TEST()
     36 {
     37 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, censor_config_test);
     38 	return MOD_SUCCESS;
     39 }
     40 	
     41 MOD_INIT()
     42 {
     43 	CmodeInfo req;
     44 
     45 	ModInfo = modinfo;
     46 
     47 	MARK_AS_OFFICIAL_MODULE(modinfo);
     48 
     49 	memset(&req, 0, sizeof(req));
     50 	req.paracount = 0;
     51 	req.is_ok = extcmode_default_requirechop;
     52 	req.letter = 'G';
     53 	CmodeAdd(modinfo->handle, req, &EXTMODE_CENSOR);
     54 
     55 	HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, censor_can_send_to_channel);
     56 	HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, censor_pre_local_part);
     57 	HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, censor_pre_local_quit);
     58 	HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, censor_stats_badwords_channel);
     59 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, censor_config_run);
     60 	return MOD_SUCCESS;
     61 }
     62 
     63 MOD_LOAD()
     64 {
     65 	return MOD_SUCCESS;
     66 }
     67 
     68 
     69 MOD_UNLOAD()
     70 {
     71 	ConfigItem_badword *badword, *next;
     72 
     73 	for (badword = conf_badword_channel; badword; badword = next)
     74 	{
     75 		next = badword->next;
     76 		DelListItem(badword, conf_badword_channel);
     77 		badword_config_free(badword);
     78 	}
     79 	return MOD_SUCCESS;
     80 }
     81 
     82 int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
     83 {
     84 	int errors = 0;
     85 	ConfigEntry *cep;
     86 	char has_word = 0, has_replace = 0, has_action = 0, action = 'r';
     87 
     88 	if (type != CONFIG_MAIN)
     89 		return 0;
     90 	
     91 	if (!ce || !ce->name || strcmp(ce->name, "badword"))
     92 		return 0; /* not interested */
     93 
     94 	if (!ce->value)
     95 	{
     96 		config_error("%s:%i: badword without type",
     97 			ce->file->filename, ce->line_number);
     98 		return 1;
     99 	}
    100 	else if (strcmp(ce->value, "channel") && 
    101 	         strcmp(ce->value, "quit") && strcmp(ce->value, "all")) {
    102 /*			config_error("%s:%i: badword with unknown type",
    103 				ce->file->filename, ce->line_number); -- can't do that.. */
    104 		return 0; /* unhandled */
    105 	}
    106 	
    107 	if (!strcmp(ce->value, "quit"))
    108 	{
    109 		config_error("%s:%i: badword quit has been removed. We just use the bad words from "
    110 		             "badword channel { } instead.",
    111 		             ce->file->filename, ce->line_number);
    112 		return 0; /* pretend unhandled.. ok not just pretend.. ;) */
    113 	}
    114 
    115 	for (cep = ce->items; cep; cep = cep->next)
    116 	{
    117 		if (config_is_blankorempty(cep, "badword"))
    118 		{
    119 			errors++;
    120 			continue;
    121 		}
    122 		if (!strcmp(cep->name, "word"))
    123 		{
    124 			const char *errbuf;
    125 			if (has_word)
    126 			{
    127 				config_warn_duplicate(cep->file->filename, 
    128 					cep->line_number, "badword::word");
    129 				continue;
    130 			}
    131 			has_word = 1;
    132 			if ((errbuf = badword_config_check_regex(cep->value,1,1)))
    133 			{
    134 				config_error("%s:%i: badword::%s contains an invalid regex: %s",
    135 					cep->file->filename,
    136 					cep->line_number,
    137 					cep->name, errbuf);
    138 				errors++;
    139 			}
    140 		}
    141 		else if (!strcmp(cep->name, "replace"))
    142 		{
    143 			if (has_replace)
    144 			{
    145 				config_warn_duplicate(cep->file->filename, 
    146 					cep->line_number, "badword::replace");
    147 				continue;
    148 			}
    149 			has_replace = 1;
    150 		}
    151 		else if (!strcmp(cep->name, "action"))
    152 		{
    153 			if (has_action)
    154 			{
    155 				config_warn_duplicate(cep->file->filename, 
    156 					cep->line_number, "badword::action");
    157 				continue;
    158 			}
    159 			has_action = 1;
    160 			if (!strcmp(cep->value, "replace"))
    161 				action = 'r';
    162 			else if (!strcmp(cep->value, "block"))
    163 				action = 'b';
    164 			else
    165 			{
    166 				config_error("%s:%d: Unknown badword::action '%s'",
    167 					cep->file->filename, cep->line_number,
    168 					cep->value);
    169 				errors++;
    170 			}
    171 				
    172 		}
    173 		else
    174 		{
    175 			config_error_unknown(cep->file->filename, cep->line_number,
    176 				"badword", cep->name);
    177 			errors++;
    178 		}
    179 	}
    180 
    181 	if (!has_word)
    182 	{
    183 		config_error_missing(ce->file->filename, ce->line_number,
    184 			"badword::word");
    185 		errors++;
    186 	}
    187 	if (has_action)
    188 	{
    189 		if (has_replace && action == 'b')
    190 		{
    191 			config_error("%s:%i: badword::action is block but badword::replace exists",
    192 				ce->file->filename, ce->line_number);
    193 			errors++;
    194 		}
    195 	}
    196 	
    197 	*errs = errors;
    198 	return errors ? -1 : 1;
    199 }
    200 
    201 
    202 int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
    203 {
    204 	ConfigEntry *cep, *word = NULL;
    205 	ConfigItem_badword *ca;
    206 
    207 	if (type != CONFIG_MAIN)
    208 		return 0;
    209 	
    210 	if (!ce || !ce->name || strcmp(ce->name, "badword"))
    211 		return 0; /* not interested */
    212 
    213 	if (strcmp(ce->value, "channel") && strcmp(ce->value, "all"))
    214 	        return 0; /* not for us */
    215 
    216 	ca = safe_alloc(sizeof(ConfigItem_badword));
    217 	ca->action = BADWORD_REPLACE;
    218 
    219 	for (cep = ce->items; cep; cep = cep->next)
    220 	{
    221 		if (!strcmp(cep->name, "action"))
    222 		{
    223 			if (!strcmp(cep->value, "block"))
    224 			{
    225 				ca->action = BADWORD_BLOCK;
    226 			}
    227 		}
    228 		else if (!strcmp(cep->name, "replace"))
    229 		{
    230 			safe_strdup(ca->replace, cep->value);
    231 		} else
    232 		if (!strcmp(cep->name, "word"))
    233 		{
    234 			word = cep;
    235 		}
    236 	}
    237 
    238 	badword_config_process(ca, word->value);
    239 
    240 	if (!strcmp(ce->value, "channel"))
    241 		AddListItem(ca, conf_badword_channel);
    242 	else if (!strcmp(ce->value, "all"))
    243 	{
    244 		AddListItem(ca, conf_badword_channel);
    245 		return 0; /* pretend we didn't see it, so other modules can handle 'all' as well */
    246 	}
    247 
    248 	return 1;
    249 }
    250 
    251 const char *stripbadwords_channel(const char *str, int *blocked)
    252 {
    253 	return stripbadwords(str, conf_badword_channel, blocked);
    254 }
    255 
    256 int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype)
    257 {
    258 	int blocked;
    259 	Hook *h;
    260 	int i;
    261 
    262 	if (!IsCensored(channel))
    263 		return HOOK_CONTINUE;
    264 
    265 	for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next)
    266 	{
    267 		i = (*(h->func.intfunc))(client, channel, BYPASS_CHANMSG_CENSOR);
    268 		if (i == HOOK_ALLOW)
    269 			return HOOK_CONTINUE; /* bypass censor restriction */
    270 		if (i != HOOK_CONTINUE)
    271 			break;
    272 	}
    273 
    274 	*msg = stripbadwords_channel(*msg, &blocked);
    275 	if (blocked)
    276 	{
    277 		*errmsg = "Swearing is not permitted in this channel";
    278 		return HOOK_DENY;
    279 	}
    280 
    281 	return HOOK_CONTINUE;
    282 }
    283 
    284 const char *censor_pre_local_part(Client *client, Channel *channel, const char *text)
    285 {
    286 	int blocked;
    287 
    288 	if (!text)
    289 		return NULL;
    290 
    291 	if (!IsCensored(channel))
    292 		return text;
    293 
    294 	text = stripbadwords_channel(text, &blocked);
    295 	return blocked ? NULL : text;
    296 }
    297 
    298 /** Is any channel where the user is in +G? */
    299 static int IsAnyChannelCensored(Client *client)
    300 {
    301 	Membership *lp;
    302 
    303 	for (lp = client->user->channel; lp; lp = lp->next)
    304 		if (IsCensored(lp->channel))
    305 			return 1;
    306 	return 0;
    307 }
    308 
    309 const char *censor_pre_local_quit(Client *client, const char *text)
    310 {
    311 	int blocked = 0;
    312 
    313 	if (!text)
    314 		return NULL;
    315 
    316 	if (IsAnyChannelCensored(client))
    317 		text = stripbadwords_channel(text, &blocked);
    318 
    319 	return blocked ? NULL : text;
    320 }
    321 
    322 int censor_stats_badwords_channel(Client *client, const char *para)
    323 {
    324 	ConfigItem_badword *words;
    325 
    326 	if (!para || !(!strcmp(para, "b") || !strcasecmp(para, "badword")))
    327 		return 0;
    328 
    329 	for (words = conf_badword_channel; words; words = words->next)
    330 	{
    331 		sendtxtnumeric(client, "c %c %s%s%s %s", words->type & BADW_TYPE_REGEX ? 'R' : 'F',
    332 		           (words->type & BADW_TYPE_FAST_L) ? "*" : "", words->word,
    333 		           (words->type & BADW_TYPE_FAST_R) ? "*" : "",
    334 		           words->action == BADWORD_REPLACE ? (words->replace ? words->replace : "<censored>") : "");
    335 	}
    336 	return 1;
    337 }