Greetings po||ux,
Here is the patch. Sorry it is a bit messy. I was not able to figure out how to edit the autoconf files so I just edited the makefile directly to add the libpcre library:
Only in ngircd/src/ngircd: censor.c
/*
Copyright (C) 2019 by jrmu <jrmu(a)lecturify.com>
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted. This software
is offered as-is, without any warranty.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcre.h>
#define OVECCOUNT 30 /* should be a multiple of 3 */
/**
* Indicates whether a message matches a list of regular expressions.
*
* @param msg The message to test
* @param msg_len The length of msg
* @param re An array of compiled regular expressions
* @param re_len The length of re
* @param out The matched string to return or NULL
* @return Boolean for whether the message should be censored.
*/
bool censor(char *msg, size_t msg_len, pcre **re, size_t re_len, char *match) {
int rc;
int ovector[OVECCOUNT];
/* actual length needed because pcre_exec scans past \0 */
int len = strnlen(msg, msg_len);
for (unsigned int i = 0; i < re_len && re[i]; i++) {
rc = pcre_exec(re[i], NULL, msg, len, 0, 0, ovector, OVECCOUNT);
if (rc >= 0) {
strlcpy(match, msg+ovector[0], ovector[1]-ovector[0]+1);
return true;
}
}
return false;
}
diff -ru ngircd-barton/src/ngircd/client.c ngircd/src/ngircd/client.c
--- ngircd-barton/src/ngircd/client.c Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/client.c Mon Sep 16 07:57:43 2019
@@ -337,7 +337,9 @@
assert(Client != NULL);
assert(Hostname != NULL);
- if (Conf_CloakHost[0]) {
+ /* Only cloak the hostmask if it has not yet been cloaked (the period
+ * indicates it's still an IP address). */
+ if (Conf_CloakHost[0] && strchr(Client->host, '.')) {
char cloak[GETID_LEN];
strlcpy(cloak, Hostname, GETID_LEN);
diff -ru ngircd-barton/src/ngircd/client.h ngircd/src/ngircd/client.h
--- ngircd-barton/src/ngircd/client.h Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/client.h Mon Sep 16 07:57:43 2019
@@ -27,7 +27,7 @@
#define CLIENT_UNKNOWNSERVER 0x0080 /* unregistered server connection */
#define CLIENT_GOTPASS_2813 0x0100 /* client did send PASS, RFC 2813 style */
#ifndef STRICT_RFC
-# define CLIENT_WAITAUTHPING 0x0200 /* waiting for AUTH PONG from client */
+#define CLIENT_WAITAUTHPING 0x0200 /* waiting for AUTH PONG from client */
#endif
#define CLIENT_WAITCAPEND 0x0400 /* waiting for "CAP END" command */
#define CLIENT_ANY 0xFFFF
diff -ru ngircd-barton/src/ngircd/conf.c ngircd/src/ngircd/conf.c
--- ngircd-barton/src/ngircd/conf.c Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/conf.c Mon Sep 16 12:22:32 2019
@@ -68,6 +68,8 @@
char *Var, char *Arg ));
static void Handle_CHANNEL PARAMS((const char *File, int Line,
char *Var, char *Arg ));
+static void Handle_CENSOR PARAMS((const char *File, int Line,
+ char *Var, char *Arg ));
static void Config_Error PARAMS((const int Level, const char *Format, ...));
@@ -332,6 +334,8 @@
bool config_valid;
size_t predef_channel_count;
struct Conf_Channel *predef_chan;
+ size_t predef_censor_count;
+ struct Conf_Censor *predef_censor;
Use_Log = false;
@@ -388,7 +392,7 @@
printf(" MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
printf(" MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
printf(" MaxNickLength = %u\n", Conf_MaxNickLength - 1);
- printf(" MaxPenaltyTime = %ld\n", Conf_MaxPenaltyTime);
+ printf(" MaxPenaltyTime = %lld\n", Conf_MaxPenaltyTime);
printf(" MaxListSize = %d\n", Conf_MaxListSize);
printf(" PingTimeout = %d\n", Conf_PingTimeout);
printf(" PongTimeout = %d\n", Conf_PongTimeout);
@@ -490,6 +494,17 @@
printf(" KeyFile = %s\n\n", predef_chan->keyfile);
}
+ predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor));
+ predef_censor = array_start(&Conf_Censors);
+
+ for (i = 0; i < predef_censor_count; i++, predef_censor++) {
+ if (!predef_censor->file)
+ continue;
+ puts( "[CENSOR]" );
+ printf(" File = %s\n\n", predef_censor->file);
+ }
+ /* "Censor" section */
+
return (config_valid ? 0 : 1);
}
@@ -757,7 +772,7 @@
strlcat(Conf_HelpFile, HELP_FILE, sizeof(Conf_HelpFile));
strcpy(Conf_ServerPwd, "");
strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
- Conf_UID = Conf_GID = 0;
+ Conf_UID = Conf_GID = 703;
/* Limits */
Conf_ConnectRetry = 60;
@@ -818,7 +833,6 @@
Conf_SyslogFacility = 0;
#endif
#endif
-
/* Initialize server configuration structures */
if (InitServers) {
for (i = 0; i < MAX_SERVERS;
@@ -901,6 +915,12 @@
int i, n;
FILE *fd;
DIR *dh;
+ size_t predef_censor_count;
+ struct Conf_Censor *predef_censor;
+ const char *error;
+ int line = 0;
+ char filters[LINE_LEN][LINE_LEN];
+ int erroffset;
Log(LOG_INFO, "Using configuration file \"%s\" ...", NGIRCd_ConfFile);
@@ -1049,6 +1069,41 @@
Conf_SSLOptions.CipherList = strdup_warn(DEFAULT_CIPHERS);
#endif
+ /* Load censor files */
+ predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor));
+ predef_censor = array_start(&Conf_Censors);
+
+ for (unsigned int i = 0; i < predef_censor_count; i++, predef_censor++) {
+// LogDebug("\n\nCensor file: %s\n", predef_censor->file);
+ if (predef_censor->file) {
+ fd = fopen(predef_censor->file, "r");
+ if (fd) {
+ while (fgets(filters[line], LINE_LEN, fd)) {
+ ngt_TrimLastChr(filters[line], '\n');
+ line++;
+ }
+ fclose(fd);
+ pcre **re = calloc(sizeof(pcre *), line);
+ if (!re) {
+ Config_Error(LOG_ERR, "Can't allocate memory for censor rules");
+ break;
+ }
+ for (int i = 0; i < line; i++) {
+ re[i] = pcre_compile(filters[i], PCRE_CASELESS, &error, &erroffset, NULL);
+ if (!re[i]) {
+ Config_Error(LOG_ERR, "Can't compile censor rules");
+ break;
+ }
+ LogDebug("Line: %d, Filter: %s, Re: %p", i, filters[i], re[i]);
+ }
+ predef_censor->re = re;
+ predef_censor->len = line;
+ } else {
+ Config_Error(LOG_ERR, "Can't read %s", predef_censor->file);
+ }
+ }
+ }
+ /* Censor filters */
return true;
}
@@ -1150,6 +1205,18 @@
continue;
}
+ if (strcasecmp(section, "[CENSOR]") == 0) {
+ count = array_length(&Conf_Censors,
+ sizeof(struct Conf_Censor));
+ if (!array_alloc(&Conf_Censors,
+ sizeof(struct Conf_Censor), count)) {
+ Config_Error(LOG_ERR,
+ "Could not allocate memory for new censor (line %d)",
+ line);
+ }
+ continue;
+ }
+
Config_Error(LOG_ERR,
"%s, line %d: Unknown section \"%s\"!",
File, line, section);
@@ -1187,6 +1254,8 @@
Handle_SERVER(File, line, var, arg);
else if (strcasecmp(section, "[CHANNEL]") == 0)
Handle_CHANNEL(File, line, var, arg);
+ else if (strcasecmp(section, "[CENSOR]") == 0)
+ Handle_CENSOR(File, line, var, arg);
else
Config_Error(LOG_ERR,
"%s, line %d: Variable \"%s\" outside section!",
@@ -1646,7 +1715,7 @@
return;
}
if (strcasecmp(Var, "MaxPenaltyTime") == 0) {
- Conf_MaxPenaltyTime = atol(Arg);
+ Conf_MaxPenaltyTime = atoll(Arg);
if (Conf_MaxPenaltyTime < -1)
Conf_MaxPenaltyTime = -1; /* "unlimited" */
return;
@@ -2193,6 +2262,40 @@
}
/**
+ * Handle variable in [CENSOR] configuration section.
+ *
+ * @param Line Line number in configuration file.
+ * @param Var Variable name.
+ * @param Arg Variable argument.
+ */
+static void
+Handle_CENSOR(const char *File, int Line, char *Var, char *Arg)
+{
+ size_t len;
+ struct Conf_Censor *censor;
+
+ assert( File != NULL );
+ assert( Line > 0 );
+ assert( Var != NULL );
+ assert( Arg != NULL );
+
+ censor = array_get(&Conf_Censors, sizeof(*censor),
+ array_length(&Conf_Censors, sizeof(*censor)) - 1);
+ if (!censor)
+ return;
+
+ if (strcasecmp(Var, "File") == 0) {
+ /* Censor file */
+ len = strlcpy(censor->file, Arg, sizeof(censor->file));
+ if (len >= sizeof(censor->file))
+ Config_Error_TooLong(File, Line, Var);
+ return;
+ }
+
+ Config_Error_Section(File, Line, Var, "Censor");
+}
+
+/**
* Validate server configuration.
*
* Please note that this function uses exit(1) on fatal errors and therefore
@@ -2280,8 +2383,8 @@
if (Conf_MaxPenaltyTime != -1)
Config_Error(LOG_WARNING,
- "Maximum penalty increase ('MaxPenaltyTime') is set to %ld, this is not recommended!",
- Conf_MaxPenaltyTime);
+ "Maximum penalty increase ('MaxPenaltyTime') is set to %lld, this is not recommended!",
+ (long long)Conf_MaxPenaltyTime);
#ifdef DEBUG
servers = servers_once = 0;
diff -ru ngircd-barton/src/ngircd/conf.h ngircd/src/ngircd/conf.h
--- ngircd-barton/src/ngircd/conf.h Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/conf.h Mon Sep 16 07:57:43 2019
@@ -27,6 +27,8 @@
#include "proc.h"
#include "conf-ssl.h"
+#include <pcre.h>
+
/**
* Configured IRC operator.
* Please note that the name of the IRC operaor and his nick have nothing to
@@ -101,7 +103,7 @@
/** Server info text */
GLOBAL char Conf_ServerInfo[CLIENT_INFO_LEN];
-/** Global server passwort */
+/** Global server password */
GLOBAL char Conf_ServerPwd[CLIENT_PASS_LEN];
/** Administrative information */
@@ -283,8 +285,16 @@
GLOBAL void Conf_DebugDump PARAMS((void));
#endif
+/** Censorship rules, one per file */
+struct Conf_Censor {
+ char file[FNAME_LEN]; /** File path */
+ pcre **re; /** Patterns to match against */
+ size_t len; /** length of regex patterns array */
+};
-#endif
+/** Array of censorship rules */
+GLOBAL array Conf_Censors;
+#endif
/* -eof- */
diff -ru ngircd-barton/src/ngircd/irc.c ngircd/src/ngircd/irc.c
--- ngircd-barton/src/ngircd/irc.c Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/irc.c Mon Sep 16 11:27:55 2019
@@ -38,6 +38,7 @@
#include "op.h"
#include "irc.h"
+#include "censor.c"
static char *Option_String PARAMS((CONN_ID Idx));
static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
@@ -511,6 +512,7 @@
#endif
} /* Option_String */
+
/**
* Send a message to target(s).
*
@@ -534,6 +536,12 @@
char *message = NULL;
char *targets[MAX_HNDL_TARGETS];
int i, target_nr = 0;
+ size_t predef_censor_count;
+ struct Conf_Censor *predef_censor;
+ char match[COMMAND_LEN];
+ char msg[COMMAND_LEN];
+ pcre **re;
+ int re_len;
assert(Client != NULL);
assert(Req != NULL);
@@ -572,6 +580,26 @@
#endif
message = Req->argv[1];
+ /* Check censorship rules */
+
+ predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor));
+ predef_censor = array_start(&Conf_Censors);
+
+ if (strlcpy(msg, message, COMMAND_LEN) >= COMMAND_LEN) {
+ LogDebug("Error copying message!");
+ } else {
+ for (unsigned int i = 0; i < predef_censor_count; i++, predef_censor++) {
+ re = predef_censor->re;
+ re_len = predef_censor->len;
+ if (re && censor(msg, COMMAND_LEN, re, re_len, match)) {
+ LogDebug("Message: %s, match: %s", msg, match);
+ snprintf(msg, COMMAND_LEN, "Your message to %s was dropped because it contains profanity or spam: %s. If this is an error, please report it in #help.", currentTarget, match);
+ currentTarget = Client_ID(Client);
+ message = msg;
+ break;
+ }
+ }
+ }
/* handle msgtarget = msgto *("," msgto) */
currentTarget = strtok_r(currentTarget, ",", &strtok_last);
ngt_UpperStr(Req->command);
diff -ru ngircd-barton/src/ngircd/ngircd.c ngircd/src/ngircd/ngircd.c
--- ngircd-barton/src/ngircd/ngircd.c Wed Sep 4 00:28:34 2019
+++ ngircd/src/ngircd/ngircd.c Mon Sep 16 07:57:43 2019
@@ -560,7 +560,7 @@
#if !defined(SINGLE_USER_OS)
/**
- * Get user and group ID of unprivileged "nobody" user.
+ * Get user and group ID of unprivileged "_ngircd" user.
*
* @param uid User ID
* @param gid Group ID
@@ -584,7 +584,7 @@
}
#endif
- pwd = getpwnam("nobody");
+ pwd = getpwnam("_ngircd");
if (!pwd)
return false;
@@ -700,11 +700,11 @@
if (Conf_UID == 0) {
pwd = getpwuid(0);
Log(LOG_INFO,
- "ServerUID must not be %s(0), using \"nobody\" instead.",
+ "ServerUID must not be %s(0), using \"_ngircd\" instead.",
pwd ? pwd->pw_name : "?");
if (!NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) {
Log(LOG_WARNING,
- "Could not get user/group ID of user \"nobody\": %s",
+ "Could not get user/group ID of user \"_ngircd\": %s",
errno ? strerror(errno) : "not found" );
goto out;
}
Only in /etc/ngircd: profanity
\b(smart|fat|dumb|big)?(ass|arse)(hol|whol)?e?s?\b
bastard
\bbdsm\b
bitch
blow\s?job
bollock
\bboners?\b
\bboo+b(ies|s)?\b
\bbugger(ed|ing)?s?\b
butt(w?hole|plug)
\bcirclejerk
\bclit(s)?\b
\bcock(s|suck.*)?\b
\bcrap(py|ped|ping|per|tastic)?\b
\b[ck]um(mer|ming|shot)?s?\b
[ck]un+il+ingus
\b[c|k]unt(s|lick.*)?\b
\b(god-?)?damn
\bdick
(tele)?dildo
\bdykes?\b
eja[ck]ulat.*
\b(dumb|nazi)?fag+(ot)?(it+)?s?\b
\bfap(ping|pening|ed)?s?\b
fellat(e|io)
\bfook(er)?s?\b
\b(m[ou]ther|m[ou]tha|father|sister|goat|cyber|finger|fist)?fuc?k+
fuc?k+(a|er|in|ing|ed|wad|head|off|whit)?s?\b
\bfcuk(er|ing)?\b
fudge\s*packers?
\bfux(or)?s?\b
gang\s*bang
gay\s*(slave|lords?|sex)
g[eo]t(ting|ten)? laid
(phone|hot|core|\banal|\boral)\s*sex
\bhorn(y|iest)
(jack|jerk)\-*\s*off
\bjizz
jizz\b
\bkock(suck.*)?s?\b
\blady\s*boys?
lmf?ao\b
mast[eu]rbat
\bmofos?\b
\bnigg(er|a)(s|z)?\b
nutsacks?
\bphu(ck|q)(ed|ing|ked)?s?\b
\bpiss(ed|ing|in|es|er|ers|off)?\b
\bpuss(e|y|ys|ies)\b
\bretard(ed)?s?\b
\brim(ming|job)?s?\b
\bscrewing\b
\bshag(ging)?\b
\bshe\-*males?
\bshit|shit\b
\bskank(y|s)?\b
\bslut(ty|ting|s)?\b
\btit+(s|y|ies|wank|ywank)\b
\btwat(head|ty)?\b
\bturds?\b
\bwank(y|er)?s?\b
\b(cam)?whor(e|ing)?(house)?s?\b
\bwtf\b
shibboleth
/etc/ngircd/ngircd.conf:
[Censor]
File = /etc/ngircd/profanity
jrmu