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 }