From 94b042e00d464c50f719945dd208c85e07e5c693 Mon Sep 17 00:00:00 2001 From: Jabber Developer Date: Mon, 25 Aug 2025 20:40:32 +0200 Subject: [PATCH] feat: Add /force-encryption command and update message handling Implement /force-encryption command to configure forced encryption settings with on|off and policy resend-to-confirm|block options. The changes enhance user experience by clarifying encryption requirements and providing actionable commands. Users can now press Enter again to confirm unencrypted messages in resend-to-confirm mode. --- src/command/cmd_defs.c | 26 ++++++++++++++++++++++++++ src/command/cmd_funcs.c | 19 +++++++++++++++++++ src/command/cmd_funcs.h | 1 + src/config/preferences.c | 9 +++++++++ src/config/preferences.h | 2 ++ src/event/client_events.c | 35 ++++++++++++++++++++++++++++++++++- src/ui/console.c | 8 ++++++++ src/ui/inputwin.c | 8 ++++++++ src/ui/inputwin.h | 1 + src/ui/ui.h | 1 + src/ui/win_types.h | 1 + src/ui/window.c | 1 + 12 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index e5fb3cf0..6730f7ba 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2729,6 +2729,7 @@ static const struct cmd_t command_defs[] = { { "os on|off", "Choose whether to include the OS name if a user asks for software information (XEP-0092)." } ) CMD_EXAMPLES( + "/privacy", "/privacy logging off", "/privacy os off") }, @@ -2744,6 +2745,31 @@ static const struct cmd_t command_defs[] = { "Redraw user interface. Can be used when some other program interrupted profanity or wrote to the same terminal and the interface looks \"broken\"." ) }, + { CMD_PREAMBLE("/force-encryption", + parse_args, 1, 3, &cons_encryption_settings) + CMD_MAINFUNC(cmd_force_encryption) + CMD_TAGS( + CMD_TAG_CHAT) + CMD_SYN( + "/force-encryption", + "/force-encryption on|off", + "/force-encryption policy resend-to-confirm|block") + CMD_DESC( + "Configure forced encryption for outgoing messages in CProof. " + "When enabled, restricts sending unencrypted messages based on the specified policy. " + "Run without arguments to display current settings.") + CMD_ARGS( + { "on|off", "Enable or disable forced encryption. When enabled, restricts unencrypted messages per the policy. When disabled, allows sending unencrypted messages without restrictions." }, + { "policy resend-to-confirm|block", "Set the policy for unencrypted messages when forced encryption is enabled. 'resend-to-confirm' requires pressing Enter again to confirm sending an unencrypted message (default). 'block' prevents sending unencrypted messages entirely." } + ) + CMD_EXAMPLES( + "/force-encryption", + "/force-encryption on", + "/force-encryption off", + "/force-encryption policy resend-to-confirm", + "/force-encryption policy block") + }, + // NEXT-COMMAND (search helper) }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index d444f3c8..e1ac8d37 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -10633,3 +10633,22 @@ cmd_vcard_save(ProfWin* window, const char* const command, gchar** args) cons_show("User vCard uploaded"); return TRUE; } + +gboolean +cmd_force_encryption(ProfWin* window, const char* const command, gchar** args) +{ + if (g_strv_length(args) == 1 && (g_strcmp0(args[0], "on") == 0 || g_strcmp0(args[0], "off") == 0)) { + _cmd_set_boolean_preference(args[0], "Forces encryption", PREF_FORCE_ENCRYPTION); + } else if (g_strv_length(args) == 2 && g_strcmp0(args[0], "policy") == 0) { + if (g_strcmp0(args[1], "resend-to-confirm") != 0 && g_strcmp0(args[1], "block") != 0) { + cons_show_error("Invalid policy: \"%s\". See '/help force-encryption' for details.", args[1]); + return TRUE; + } + prefs_set_string(PREF_FORCE_ENCRYPTION_MODE, args[1]); + cons_show("Force encryption policy has been set to \"%s\".", args[1]); + } else { + cons_bad_cmd_usage(command); + } + + return TRUE; +} \ No newline at end of file diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 5da0a6c7..b8b74e3c 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -269,5 +269,6 @@ gboolean cmd_vcard_photo(ProfWin* window, const char* const command, gchar** arg gboolean cmd_vcard_refresh(ProfWin* window, const char* const command, gchar** args); gboolean cmd_vcard_set(ProfWin* window, const char* const command, gchar** args); gboolean cmd_vcard_save(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_force_encryption(ProfWin* window, const char* const command, gchar** args); #endif diff --git a/src/config/preferences.c b/src/config/preferences.c index d306066b..bad809c8 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1792,6 +1792,8 @@ _get_group(preference_t pref) case PREF_OUTGOING_STAMP: case PREF_INCOMING_STAMP: case PREF_MOOD: + case PREF_FORCE_ENCRYPTION: + case PREF_FORCE_ENCRYPTION_MODE: return PREF_GROUP_UI; case PREF_STATES: case PREF_OUTTYPE: @@ -2150,6 +2152,10 @@ _get_key(preference_t pref) return "strophe.sm.enabled"; case PREF_STROPHE_SM_RESEND: return "strophe.sm.resend"; + case PREF_FORCE_ENCRYPTION: + return "force-encryption.enabled"; + case PREF_FORCE_ENCRYPTION_MODE: + return "force-encryption.policy"; default: return NULL; } @@ -2204,6 +2210,7 @@ _get_default_boolean(preference_t pref) case PREF_STROPHE_SM_RESEND: return TRUE; case PREF_PGP_PUBKEY_AUTOIMPORT: + case PREF_FORCE_ENCRYPTION: default: return FALSE; } @@ -2310,6 +2317,8 @@ _get_default_string(preference_t pref) return "0"; case PREF_DBLOG: return "on"; + case PREF_FORCE_ENCRYPTION_MODE: + return "resend-to-confirm"; default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index e5904149..750a87ee 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -187,6 +187,8 @@ typedef enum { PREF_STROPHE_SM_RESEND, PREF_VCARD_PHOTO_CMD, PREF_STATUSBAR_TABMODE, + PREF_FORCE_ENCRYPTION, + PREF_FORCE_ENCRYPTION_MODE } preference_t; typedef struct prof_alias_t diff --git a/src/event/client_events.c b/src/event/client_events.c index 3250160b..cae3d46d 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -45,6 +45,7 @@ #include "config/preferences.h" #include "event/common.h" #include "plugins/plugins.h" +#include "ui/inputwin.h" #include "ui/window_list.h" #include "xmpp/chat_session.h" #include "xmpp/session.h" @@ -129,6 +130,38 @@ cl_ev_presence_send(const resource_presence_t presence_type, const int idle_secs presence_send(presence_type, idle_secs, signed_status); } +// Checks if an unencrypted message can be sent based on encryption preferences. +// Returns TRUE if allowed, FALSE if blocked. +static gboolean +_allow_unencrypted_message(ProfChatWin* chatwin, const char* const msg) +{ + if (!prefs_get_boolean(PREF_FORCE_ENCRYPTION)) { + return TRUE; // Encryption not enforced + } + + auto_gchar gchar* force_enc_mode = prefs_get_string(PREF_FORCE_ENCRYPTION_MODE); + + if (g_strcmp0(force_enc_mode, "block") == 0) { + win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: encryption required. Enable (omemo/pgp/otr) encryption or /force-encryption off."); + return FALSE; + } + + if (g_strcmp0(force_enc_mode, "resend-to-confirm") == 0) { + // We do not use chatwin->last_message because it should be set only if it's sent. + guint msg_hash = g_str_hash(msg); + if (msg_hash != chatwin->last_attempted_msg_hash) { + win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: encryption required. Enable (omemo/pgp/otr) encryption or press Enter again to send without encryption. Use /force-encryption off to disable this protection."); + chatwin->last_attempted_msg_hash = msg_hash; + inp_set_line(msg); // reset message to prevent the need to retype it + return FALSE; + } + return TRUE; + } + + win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: invalid encryption mode (%s). Use '/force-encryption policy resend-to-confirm'.", force_enc_mode); + return FALSE; +} + void cl_ev_send_msg_correct(ProfChatWin* chatwin, const char* const msg, const char* const oob_url, gboolean correct_last_msg) { @@ -177,7 +210,7 @@ cl_ev_send_msg_correct(ProfChatWin* chatwin, const char* const msg, const char* #ifdef HAVE_LIBOTR handled = otr_on_message_send(chatwin, message, request_receipt, replace_id); #endif - if (!handled) { + if (!handled && _allow_unencrypted_message(chatwin, message)) { auto_char char* id = message_send_chat(chatwin->barejid, message, oob_url, request_receipt, replace_id); chat_log_msg_out(chatwin->barejid, message, NULL); log_database_add_outgoing_chat(id, chatwin->barejid, message, replace_id, PROF_MSG_ENC_NONE); diff --git a/src/ui/console.c b/src/ui/console.c index 3ed256e1..352906f3 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2891,3 +2891,11 @@ cons_privacy_setting(void) } } } + +void +cons_encryption_settings(void) +{ + cons_show("Force encryption : %sabled", prefs_get_boolean(PREF_FORCE_ENCRYPTION) ? "en" : "dis"); + + cons_show("Force encryption mode : %s", prefs_get_string(PREF_FORCE_ENCRYPTION_MODE)); +} \ No newline at end of file diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 6eddc177..c1765d11 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -294,6 +294,14 @@ inp_get_line(void) return line; } +void +inp_set_line(const char* const new_line) +{ + rl_replace_line(new_line, 1); + rl_point = rl_end; + _inp_redisplay(); +} + char* inp_get_password(void) { diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h index 3aefcc0a..5ee034f7 100644 --- a/src/ui/inputwin.h +++ b/src/ui/inputwin.h @@ -46,5 +46,6 @@ void inp_win_resize(void); void inp_put_back(void); char* inp_get_password(void); char* inp_get_line(void); +void inp_set_line(const char* const new_line); #endif diff --git a/src/ui/ui.h b/src/ui/ui.h index 97b7c32e..f2f30870 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -351,6 +351,7 @@ void cons_theme_properties(void); void cons_theme_colours(void); void cons_show_tlscert(const TLSCertificate* cert); void cons_show_tlscert_summary(const TLSCertificate* cert); +void cons_encryption_settings(void); void cons_alert(ProfWin* alert_origin_window); void cons_remove_alert(ProfWin* window); diff --git a/src/ui/win_types.h b/src/ui/win_types.h index dc821c01..a7e0174e 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -188,6 +188,7 @@ typedef struct prof_chat_win_t char* last_message; char* last_msg_id; gboolean has_attention; + guint last_attempted_msg_hash; } ProfChatWin; typedef struct prof_muc_win_t diff --git a/src/ui/window.c b/src/ui/window.c index c8742926..fad8535c 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -164,6 +164,7 @@ win_create_chat(const char* const barejid) new_win->last_message = NULL; new_win->last_msg_id = NULL; new_win->has_attention = FALSE; + new_win->last_attempted_msg_hash = 0; new_win->memcheck = PROFCHATWIN_MEMCHECK; return &new_win->window;