Skip to content

Commit 70b0041

Browse files
committed
feat: Add word block list to config
A list of blocked words can now be added to the config file. Any message that contains a word on the list will not be able to send, and will cause an error message to be displayed.
1 parent 5e17fc4 commit 70b0041

23 files changed

+239
-56
lines changed

doc/toxic.conf.5

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
.\" Title: toxic.conf
33
.\" Author: [see the "AUTHORS" section]
44
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
5-
.\" Date: 2024-02-09
5+
.\" Date: 2024-12-23
66
.\" Manual: Toxic Manual
77
.\" Source: toxic __VERSION__
88
.\" Language: English
99
.\"
10-
.TH "TOXIC\&.CONF" "5" "2024\-02\-09" "toxic __VERSION__" "Toxic Manual"
10+
.TH "TOXIC\&.CONF" "5" "2024\-12\-23" "toxic __VERSION__" "Toxic Manual"
1111
.\" -----------------------------------------------------------------
1212
.\" * Define some portability stuff
1313
.\" -----------------------------------------------------------------
@@ -368,6 +368,11 @@ The colour of the conferences\(cqs tab window name\&. (black, white, gray, brown
368368
.RE
369369
.RE
370370
.PP
371+
\fBblocked_words\fR
372+
.RS 4
373+
A list of case\-insensitive words that cannot be sent in messages\&. String value\&. Must be enclosed in double quotes and must be ⇐ 256 characters\&.
374+
.RE
375+
.PP
371376
\fBsounds\fR
372377
.RS 4
373378
Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&.

doc/toxic.conf.5.asc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ OPTIONS
229229
*tab_name_colour*;;
230230
The colour of the conferences's tab window name. (black, white, gray, brown, red, green, blue, cyan, yellow, magenta, orange, pink)
231231

232+
*blocked_words*::
233+
A list of case-insensitive words that cannot be sent in messages. String value. Must be enclosed in double quotes and must be <= 256 characters.
234+
232235
*sounds*::
233236
Configuration related to notification sounds.
234237
Special value "silent" can be used to disable a specific notification. +

misc/toxic.conf.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,15 @@ conferences = {
206206
// };
207207
};
208208

209+
// A list of case-insensitive words that cannot be sent in messages. If you attempt to send
210+
// a message that contains a word in this list to a group, friend, etc., the message will not
211+
// send and an error will be displayed. Words must be enclosed in double quotes and must not
212+
// be longer than 256 characters.
213+
blocked_words = [
214+
// "hunter2",
215+
// "certainly!"
216+
];
217+
209218
// To disable a sound set the path to "silent"
210219
sounds = {
211220
error="__DATADIR__/sounds/ToxicError.wav";

src/api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,5 @@ void invoke_autoruns(ToxWindow *self, const char *autorun_path)
219219

220220
closedir(d);
221221
}
222+
222223
#endif /* PYTHON */

src/audio_call.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ void callback_recv_invite(Toxic *toxic, uint32_t friend_number)
449449
}
450450
}
451451
}
452+
452453
void callback_recv_ringing(Toxic *toxic, uint32_t friend_number)
453454
{
454455
if (friend_number >= CallControl.max_calls) {
@@ -466,6 +467,7 @@ void callback_recv_ringing(Toxic *toxic, uint32_t friend_number)
466467
}
467468
}
468469
}
470+
469471
void callback_recv_starting(Toxic *toxic, uint32_t friend_number)
470472
{
471473
if (friend_number >= CallControl.max_calls) {
@@ -484,6 +486,7 @@ void callback_recv_starting(Toxic *toxic, uint32_t friend_number)
484486
}
485487
}
486488
}
489+
487490
void callback_call_started(Toxic *toxic, uint32_t friend_number)
488491
{
489492
if (friend_number >= CallControl.max_calls) {
@@ -502,6 +505,7 @@ void callback_call_started(Toxic *toxic, uint32_t friend_number)
502505
}
503506
}
504507
}
508+
505509
void callback_call_canceled(Toxic *toxic, uint32_t friend_number)
506510
{
507511
if (friend_number >= CallControl.max_calls) {
@@ -519,6 +523,7 @@ void callback_call_canceled(Toxic *toxic, uint32_t friend_number)
519523
}
520524
}
521525
}
526+
522527
void callback_call_rejected(Toxic *toxic, uint32_t friend_number)
523528
{
524529
if (friend_number >= CallControl.max_calls) {
@@ -536,6 +541,7 @@ void callback_call_rejected(Toxic *toxic, uint32_t friend_number)
536541
}
537542
}
538543
}
544+
539545
void callback_call_ended(Toxic *toxic, uint32_t friend_number)
540546
{
541547
if (friend_number >= CallControl.max_calls) {

src/audio_device.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ void get_al_device_names(void)
200200

201201
if (stringed_device_list != NULL) {
202202
audio_state->default_al_device_name[type] = alcGetString(NULL,
203-
type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER);
203+
type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER);
204204

205205
for (; *stringed_device_list != '\0'
206206
&& audio_state->num_al_devices[type] < MAX_OPENAL_DEVICES; ++audio_state->num_al_devices[type]) {
@@ -341,7 +341,7 @@ static DeviceError open_al_device(DeviceType type, FrameInfo frame_info)
341341
{
342342
audio_state->al_device[type] = type == input
343343
? alcCaptureOpenDevice(audio_state->current_al_device_name[type],
344-
frame_info.sample_rate, sound_mode(frame_info.stereo), frame_info.samples_per_frame * 2)
344+
frame_info.sample_rate, sound_mode(frame_info.stereo), frame_info.samples_per_frame * 2)
345345
: alcOpenDevice(audio_state->current_al_device_name[type]);
346346

347347
if (audio_state->al_device[type] == NULL) {
@@ -754,6 +754,7 @@ static void *poll_input(void *arg)
754754

755755
pthread_exit(NULL);
756756
}
757+
757758
#endif
758759

759760
float get_input_volume(void)

src/chat.c

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,16 +1394,19 @@ static bool chat_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr)
13941394
input_ret = true;
13951395
rm_trailing_spaces_buf(ctx);
13961396

1397-
if (!wstring_is_empty(ctx->line)) {
1398-
add_line_to_hist(ctx);
1397+
wstrsubst(ctx->line, L'¶', L'\n');
13991398

1400-
wstrsubst(ctx->line, L'¶', L'\n');
1399+
char line[MAX_STR_SIZE];
14011400

1402-
char line[MAX_STR_SIZE];
1401+
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1402+
memset(line, 0, sizeof(line));
1403+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
1404+
}
14031405

1404-
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1405-
memset(line, 0, sizeof(line));
1406-
}
1406+
const bool contains_blocked_word = string_contains_blocked_word(line, &toxic->client_data);
1407+
1408+
if (line[0] != '\0' && !contains_blocked_word) {
1409+
add_line_to_hist(ctx);
14071410

14081411
if (line[0] == '/') {
14091412
if (strcmp(line, "/close") == 0) {
@@ -1414,7 +1417,7 @@ static bool chat_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr)
14141417
} else {
14151418
execute(ctx->history, self, toxic, line, CHAT_COMMAND_MODE);
14161419
}
1417-
} else if (line[0]) {
1420+
} else {
14181421
char selfname[TOX_MAX_NAME_LENGTH + 1];
14191422
tox_self_get_name(tox, (uint8_t *) selfname);
14201423

@@ -1423,14 +1426,16 @@ static bool chat_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr)
14231426

14241427
const int id = line_info_add(self, c_config, true, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
14251428
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
1426-
} else {
1427-
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
14281429
}
14291430
}
14301431

1431-
wclear(ctx->linewin);
1432-
wmove(self->window, y2, 0);
1433-
reset_buf(ctx);
1432+
if (!contains_blocked_word) {
1433+
wclear(ctx->linewin);
1434+
wmove(self->window, y2, 0);
1435+
reset_buf(ctx);
1436+
} else {
1437+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, "* Message contains blocked word");
1438+
}
14341439
}
14351440

14361441
if (ctx->len <= 0 && ctx->self_is_typing) {

src/conference.c

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,16 +1042,19 @@ static bool conference_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr
10421042
input_ret = true;
10431043
rm_trailing_spaces_buf(ctx);
10441044

1045-
if (!wstring_is_empty(ctx->line)) {
1046-
add_line_to_hist(ctx);
1045+
wstrsubst(ctx->line, L'¶', L'\n');
10471046

1048-
wstrsubst(ctx->line, L'¶', L'\n');
1047+
char line[MAX_STR_SIZE];
10491048

1050-
char line[MAX_STR_SIZE];
1049+
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1050+
memset(line, 0, sizeof(line));
1051+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
1052+
}
10511053

1052-
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1053-
memset(line, 0, sizeof(line));
1054-
}
1054+
const bool contains_blocked_word = string_contains_blocked_word(line, &toxic->client_data);
1055+
1056+
if (line[0] != '\0' && !contains_blocked_word) {
1057+
add_line_to_hist(ctx);
10551058

10561059
if (line[0] == '/') {
10571060
if (strcmp(line, "/close") == 0) {
@@ -1062,20 +1065,22 @@ static bool conference_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr
10621065
} else {
10631066
execute(ctx->history, self, toxic, line, CONFERENCE_COMMAND_MODE);
10641067
}
1065-
} else if (line[0]) {
1068+
} else {
10661069
Tox_Err_Conference_Send_Message err;
10671070

10681071
if (!tox_conference_send_message(tox, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
10691072
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
10701073
}
1071-
} else {
1072-
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
10731074
}
10741075
}
10751076

1076-
wclear(ctx->linewin);
1077-
wmove(self->window, y2, 0);
1078-
reset_buf(ctx);
1077+
if (!contains_blocked_word) {
1078+
wclear(ctx->linewin);
1079+
wmove(self->window, y2, 0);
1080+
reset_buf(ctx);
1081+
} else {
1082+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, "* Message contains blocked word");
1083+
}
10791084
}
10801085

10811086
return input_ret;

src/groupchats.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,16 +1986,19 @@ static bool groupchat_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr)
19861986
input_ret = true;
19871987
rm_trailing_spaces_buf(ctx);
19881988

1989-
if (!wstring_is_empty(ctx->line)) {
1990-
add_line_to_hist(ctx);
1989+
wstrsubst(ctx->line, L'¶', L'\n');
19911990

1992-
wstrsubst(ctx->line, L'¶', L'\n');
1991+
char line[MAX_STR_SIZE];
19931992

1994-
char line[MAX_STR_SIZE];
1993+
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1994+
memset(line, 0, sizeof(line));
1995+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
1996+
}
19951997

1996-
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
1997-
memset(line, 0, sizeof(line));
1998-
}
1998+
const bool contains_blocked_word = string_contains_blocked_word(line, &toxic->client_data);
1999+
2000+
if (line[0] != '\0' && !contains_blocked_word) {
2001+
add_line_to_hist(ctx);
19992002

20002003
if (line[0] == '/') {
20012004
if (strncmp(line, "/close", strlen("/close")) == 0) {
@@ -2023,15 +2026,17 @@ static bool groupchat_onKey(ToxWindow *self, Toxic *toxic, wint_t key, bool ltr)
20232026
} else {
20242027
execute(ctx->history, self, toxic, line, GROUPCHAT_COMMAND_MODE);
20252028
}
2026-
} else if (line[0]) {
2027-
send_group_message(self, toxic, self->num, line, TOX_MESSAGE_TYPE_NORMAL);
20282029
} else {
2029-
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
2030+
send_group_message(self, toxic, self->num, line, TOX_MESSAGE_TYPE_NORMAL);
20302031
}
2032+
}
20312033

2034+
if (!contains_blocked_word) {
20322035
wclear(ctx->linewin);
20332036
wmove(self->window, y2, 0);
20342037
reset_buf(ctx);
2038+
} else {
2039+
line_info_add(self, c_config, false, NULL, NULL, SYS_MSG, 0, RED, "* Message contains blocked word");
20352040
}
20362041
}
20372042

src/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,12 @@ int main(int argc, char **argv)
13291329
init_queue_add(init_q, "Failed to load conference config settings: error %d", cs_ret);
13301330
}
13311331

1332+
const int bl_ret = settings_load_blocked_words(&toxic->client_data, run_opts);
1333+
1334+
if (bl_ret != 0) {
1335+
init_queue_add(init_q, "Failed to load blocked words list: error %d", bl_ret);
1336+
}
1337+
13321338
set_active_window_by_type(windows, WINDOW_TYPE_PROMPT);
13331339

13341340
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {

src/misc_tools.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* under the GNU General Public License 3.0.
77
*/
88

9+
#ifndef _GNU_SOURCE
10+
#define _GNU_SOURCE /* needed for strcasestr() */
11+
#endif
12+
913
#include <assert.h>
1014
#include <arpa/inet.h>
1115
#include <ctype.h>
@@ -679,7 +683,7 @@ void free_ptr_array(void **arr)
679683

680684
void **tmp = arr;
681685

682-
while (*arr) {
686+
while (*arr != NULL) {
683687
free(*arr);
684688
++arr;
685689
}
@@ -789,3 +793,14 @@ size_t format_time_str(char *s, size_t max, const char *format, const struct tm
789793
return strftime(s, max, format, tm);
790794
#pragma GCC diagnostic pop
791795
}
796+
797+
bool string_contains_blocked_word(const char *line, const Client_Data *client_data)
798+
{
799+
for (size_t i = 0; i < client_data->num_blocked_words; ++i) {
800+
if (strcasestr(line, client_data->blocked_words[i]) != NULL) {
801+
return true;
802+
}
803+
}
804+
805+
return false;
806+
}

src/misc_tools.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,14 @@ int colour_string_to_int(const char *colour);
293293
*/
294294
size_t format_time_str(char *s, size_t max, const char *format, const struct tm *tm);
295295

296+
/*
297+
* Returns true if `line` contains a word that's in the client's blocked words list.
298+
*/
299+
bool string_contains_blocked_word(const char *line, const Client_Data *client_data);
300+
296301
#ifdef __cplusplus
297302
} /* extern "C" */
303+
298304
#endif /* __cplusplus */
299305

300306
#endif /* MISC_TOOLS_H */

src/notify.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static struct _ActiveNotifications {
9090
time_t n_timeout;
9191
#endif /* BOX_NOTIFY */
9292
} actives[ACTIVE_NOTIFS_MAX];
93+
9394
/**********************************************************************************/
9495
/**********************************************************************************/
9596
/**********************************************************************************/
@@ -582,6 +583,7 @@ void stop_sound(int id)
582583
clear_actives_index(id);
583584
}
584585
}
586+
585587
#endif /* SOUND_NOTIFY */
586588

587589
static int m_play_sound(const Client_Config *c_config, Notification notif, uint64_t flags)

0 commit comments

Comments
 (0)