Compare commits

..

6 Commits

Author SHA1 Message Date
4a5dd4dd69 Fix(ui.h): cons_show_qrcode declaration. Minor change
Some checks failed
CI / Check spelling (pull_request) Successful in 17s
CI / Check coding style (pull_request) Successful in 32s
CI / Linux (debian) (pull_request) Failing after 1m19s
CI / Linux (ubuntu) (pull_request) Successful in 10m15s
CI / Linux (arch) (pull_request) Successful in 12m3s
Prior to the change declaration did not include parameters, leading to errors in static analyzers and potential issues with compilation.
2025-09-01 23:00:51 +02:00
ac2b6b9635 fix(cmd_sub): Prevent crash with malformed JID in /sub command
Added null check for jidp in cmd_sub to handle jid_create returning NULL.

Crash occurred when processing malformed JID inputs like @example.com.

Ensures robust handling of invalid JIDs.

Fixes #23
2025-09-01 22:59:35 +02:00
51d2355d97 fix(cmd_caps): Prevent crash in /caps with malformed JID input
All checks were successful
CI / Check spelling (pull_request) Successful in 15s
CI / Check coding style (pull_request) Successful in 29s
CI / Linux (debian) (pull_request) Successful in 9m54s
CI / Linux (ubuntu) (pull_request) Successful in 10m15s
CI / Linux (arch) (pull_request) Successful in 11m51s
CI / Check spelling (push) Successful in 18s
CI / Check coding style (push) Successful in 31s
CI / Linux (debian) (push) Successful in 10m35s
CI / Linux (ubuntu) (push) Successful in 11m0s
CI / Linux (arch) (push) Successful in 13m26s
- Added null check for `jid` in `cmd_caps` to handle `jid_create` returning NULL.
- Crash occurred when processing malformed inputs like `@example.com` in `/caps`.
- Ensures robust handling of invalid JID strings in `WIN_CHAT` and `WIN_CONSOLE` cases.

Fixes #20
2025-09-01 21:28:19 +02:00
e33c7a477d docs(rebranding): update readme
All checks were successful
CI / Check spelling (push) Successful in 16s
CI / Check coding style (push) Successful in 32s
CI / Linux (debian) (push) Successful in 10m39s
CI / Linux (arch) (push) Successful in 13m3s
CI / Linux (ubuntu) (push) Successful in 13m7s
2025-09-01 16:57:10 +02:00
f4405d114a Add unit tests for forced encryption check function
All checks were successful
CI / Check coding style (pull_request) Successful in 35s
CI / Check spelling (pull_request) Successful in 18s
CI / Linux (debian) (pull_request) Successful in 10m28s
CI / Linux (ubuntu) (pull_request) Successful in 10m51s
CI / Linux (arch) (pull_request) Successful in 13m17s
CI / Check spelling (push) Successful in 17s
CI / Check coding style (push) Successful in 33s
CI / Linux (arch) (push) Successful in 12m53s
CI / Linux (ubuntu) (push) Successful in 12m56s
CI / Linux (debian) (push) Successful in 13m4s
2025-08-27 16:48:27 +02:00
442bfb6ce1 feat: Add /force-encryption command and update message handling
All checks were successful
CI / Check spelling (pull_request) Successful in 18s
CI / Check coding style (pull_request) Successful in 34s
CI / Linux (ubuntu) (pull_request) Successful in 14m5s
CI / Linux (debian) (pull_request) Successful in 14m5s
CI / Linux (arch) (pull_request) Successful in 14m53s
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.
2025-08-25 21:11:27 +02:00
23 changed files with 359 additions and 26 deletions

View File

@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
flavor: [arch, debian, fedora, ubuntu]
flavor: [arch, debian, ubuntu]
name: Linux
steps:

View File

@@ -166,6 +166,7 @@ unittest_sources = \
tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \
tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \
tests/unittests/test_plugins_disco.c tests/unittests/test_plugins_disco.h \
tests/unittests/test_forced_encryption.c tests/unittests/test_forced_encryption.h \
tests/unittests/unittests.c
functionaltest_sources = \

View File

@@ -1,40 +1,36 @@
# Profanity
![Build Status](https://github.com/profanity-im/profanity/workflows/CI/badge.svg) [![GitHub contributors](https://img.shields.io/github/contributors/profanity-im/profanity.svg)](https://github.com/profanity-im/profanity/graphs/contributors/) [![Packaging status](https://repology.org/badge/tiny-repos/profanity.svg)](https://repology.org/project/profanity/versions)
# CProof
![Build Status](https://git.jabber.space/devs/cproof/actions/workflows/main.yml/badge.svg)
Profanity is a console based XMPP client inspired by [Irssi](http://www.irssi.org/).
CProof is a console based XMPP chat client based on [Profanity](https://profanity-im.github.io/).
![alt tag](https://profanity-im.github.io/images/prof-2.png)
![alt tag](https://jabber.space/images/CProof-ui.png)
See the [User Guide](https://profanity-im.github.io/userguide.html) for information on installing and using Profanity.
See the [Quick Start Guide](https://jabber.space/getting-started/quick-start/) for information on installing and using CProof.
## Project
This project is about freedom, privacy and choice. We want to enable people to chat with one another in a safe way. Thus supporting encryption (OTR, PGP, OMEMO, OX) and being decentralized, meaning everyone can run their own server. We believe [XMPP](https://xmpp.org/) is a great proven protocol with an excellent community serving this purpose well.
CProof enables you to communicate with privacy, freedom and comfort. Our open-source chat application delivers secure, end-to-end encrypted messaging (OTR, PGP, OMEMO, OX) built on the trusted [XMPP](https://xmpp.org/) protocol. With a decentralized design, you can connect directly or even host your own server, keeping your data in your hands. Whether you're chatting with friends or collaborating securely, CProof makes private communication simple, reliable, and truly yours.
## Installation
Our [user guide](https://profanity-im.github.io/userguide.html) contains an [install](https://profanity-im.github.io/guide/latest/install.html) section and a section for [building](https://profanity-im.github.io/guide/latest/build.html) from source yourself.
Check our [installation guide](https://jabber.space/getting-started/installation/) for detailed instructions.
## How to contribute
We tried to sum things up on our [helpout](https://profanity-im.github.io/helpout.html) page.
Additionally you can check out our [blog](https://profanity-im.github.io/blog/) where we have articles like:
[How to get a backtrace](https://profanity-im.github.io/blog/post/how-to-get-a-backtrace/) and [Contributing a Patch via GitHub](https://profanity-im.github.io/blog/post/contributing-a-patch-via-github/).
For more technical details check out our [CONTRIBUTING.md](CONTRIBUTING.md) file.
See our [Helping Out](https://jabber.space/contributing/helpout/) page for a concise summary of ways to help us.
Review the [Contributing Guide](CONTRIBUTING.md) and [Code Overview](https://jabber.space/contributing/code-overview/) pages for advanced technical details.
## Getting help
To get help, first read our [User Guide](https://profanity-im.github.io/userguide.html) then check out the [FAQ](https://profanity-im.github.io/faq.html).
If you are having a problem then first search the [issue tracker](https://github.com/profanity-im/profanity/issues).
If you don't find anything there either come to our [MUC](xmpp:profanity@rooms.dismail.de?join) or create a new issue depending on what your problem is.
Prior to asking questions, check our [User Guide](https://profanity-im.github.io/userguide.html), then check out the [FAQ](https://jabber.space/about/faq/).
If you are still having a problem then search the [issue tracker](https://git.jabber.space/devs/cproof/issues).
As a last resort, feel free to write us on [support@jabber.tech](mailto:support@jabber.tech) or create a new issue depending on what your problem is.
## Links
### Website
<!-- TODO:
URL: https://jabber.space/
-->
Feel free to visit our website: [jabber.space](https://jabber.space/).
Repo: https://git.jabber.space/devs/profanity
<!-- TODO: blog/docs link -->
You may also check the repository if you like, available on [git.jabber.space/devs/profanity](https://git.jabber.space/devs/profanity).
### Plugins
Plugins repository: https://github.com/profanity-im/profanity-plugins
Plugins repository: https://git.jabber.space/devs/cproof-plugins

View File

@@ -136,6 +136,7 @@ static char* _mood_autocomplete(ProfWin* window, const char* const input, gboole
static char* _strophe_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _adhoc_cmd_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _force_encryption_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _script_autocomplete_func(const char* const prefix, gboolean previous, void* context);
@@ -296,6 +297,8 @@ static Autocomplete vcard_name_ac;
static Autocomplete vcard_set_param_ac;
static Autocomplete vcard_togglable_param_ac;
static Autocomplete vcard_address_type_ac;
static Autocomplete force_encryption_ac;
static Autocomplete force_encryption_policy_ac;
static Autocomplete* all_acs[] = {
&commands_ac,
@@ -448,6 +451,8 @@ static Autocomplete* all_acs[] = {
&vcard_set_param_ac,
&vcard_togglable_param_ac,
&vcard_address_type_ac,
&force_encryption_ac,
&force_encryption_policy_ac
};
static GHashTable* ac_funcs = NULL;
@@ -1343,6 +1348,13 @@ cmd_ac_init(void)
autocomplete_add(vcard_address_type_ac, "domestic");
autocomplete_add(vcard_address_type_ac, "international");
autocomplete_add(force_encryption_ac, "on");
autocomplete_add(force_encryption_ac, "off");
autocomplete_add(force_encryption_ac, "policy");
autocomplete_add(force_encryption_policy_ac, "block");
autocomplete_add(force_encryption_policy_ac, "resend-to-confirm");
if (ac_funcs != NULL)
g_hash_table_destroy(ac_funcs);
ac_funcs = g_hash_table_new(g_str_hash, g_str_equal);
@@ -1415,6 +1427,7 @@ cmd_ac_init(void)
g_hash_table_insert(ac_funcs, "/win", _win_autocomplete);
g_hash_table_insert(ac_funcs, "/wins", _wins_autocomplete);
g_hash_table_insert(ac_funcs, "/wintitle", _wintitle_autocomplete);
g_hash_table_insert(ac_funcs, "/force-encryption", _force_encryption_autocomplete);
}
void
@@ -4406,3 +4419,17 @@ _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous)
return result;
}
static char*
_force_encryption_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/force-encryption policy", force_encryption_policy_ac, TRUE, previous);
if (result) {
return result;
}
result = autocomplete_param_with_ac(input, "/force-encryption", force_encryption_ac, TRUE, previous);
return result;
}

View File

@@ -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_setting)
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)
};

View File

@@ -1263,6 +1263,11 @@ cmd_sub(ProfWin* window, const char* const command, gchar** args)
auto_jid Jid* jidp = jid_create(jid);
if (jidp == NULL) {
cons_show("Malformed JID: %s", jid);
return TRUE;
}
if (strcmp(subcmd, "allow") == 0) {
presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBED);
cons_show("Accepted subscription for %s", jidp->barejid);
@@ -3418,7 +3423,7 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
if (args[0]) {
auto_jid Jid* jid = jid_create(args[0]);
if (jid->fulljid == NULL) {
if (jid == NULL || jid->fulljid == NULL) {
cons_show("You must provide a full jid to the /caps command.");
} else {
PContact pcontact = roster_get_contact(jid->barejid);
@@ -10633,3 +10638,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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -42,9 +42,11 @@
#include "log.h"
#include "chatlog.h"
#include "database.h"
#include "client_events.h"
#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"
@@ -177,7 +179,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);
@@ -251,3 +253,34 @@ cl_ev_send_priv_msg(ProfPrivateWin* privwin, const char* const msg, const char*
plugins_post_priv_message_send(privwin->fulljid, message);
}
}
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) {
chatwin->last_attempted_msg_hash = 0; // reset hash
return TRUE;
}
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;
}
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;
}

View File

@@ -52,4 +52,8 @@ void cl_ev_send_muc_msg_corrected(ProfMucWin* mucwin, const char* const msg, con
void cl_ev_send_muc_msg(ProfMucWin* mucwin, const char* const msg, const char* const oob_url);
void cl_ev_send_priv_msg(ProfPrivateWin* privwin, const char* const msg, const char* const oob_url);
// Checks if an unencrypted message can be sent based on encryption preferences.
// Returns TRUE if allowed, FALSE if blocked.
gboolean allow_unencrypted_message(ProfChatWin* chatwin, const char* const msg);
#endif

View File

@@ -2891,3 +2891,11 @@ cons_privacy_setting(void)
}
}
}
void
cons_encryption_setting(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));
}

View File

@@ -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)
{

View File

@@ -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

View File

@@ -289,7 +289,7 @@ void cons_show_disco_info(const char* from, GSList* identities, GSList* features
void cons_show_disco_contact_information(GHashTable* addresses);
void cons_show_qrcode();
void cons_show_qrcode(const char* const text);
void cons_show_room_invite(const char* const invitor, const char* const room, const char* const reason);
void cons_check_version(gboolean not_available_msg);
@@ -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_setting(void);
void cons_alert(ProfWin* alert_origin_window);
void cons_remove_alert(ProfWin* window);

View File

@@ -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

View File

@@ -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;

View File

@@ -40,3 +40,16 @@ cmd_sub_shows_usage_when_no_arg(void** state)
gboolean result = cmd_sub(NULL, CMD_SUB, args);
assert_true(result);
}
void
test_cmd_sub_malformed_jid(void** state)
{
ProfWin win;
gchar* args[] = { "request", "@example.com" };
will_return(connection_get_status, JABBER_CONNECTED);
expect_cons_show("Malformed JID: @example.com");
gboolean result = cmd_sub((ProfWin*)&win, CMD_SUB, args);
assert_true(result);
}

View File

@@ -1,2 +1,3 @@
void cmd_sub_shows_message_when_not_connected(void** state);
void cmd_sub_shows_usage_when_no_arg(void** state);
void test_cmd_sub_malformed_jid(void** state);

View File

@@ -0,0 +1,135 @@
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <glib.h>
#include "event/client_events.h"
#include "command/cmd_funcs.h"
#include "config/preferences.h"
#include "ui/stub_ui.h"
#define CMD_FORCE_ENCRYPTION "/force-encryption"
// Test: /force-encryption with no arguments shows error.
void
test_cmd_force_encryption_no_args_bad_cmd(void** state)
{
char* args[] = { NULL };
expect_string(cons_bad_cmd_usage, cmd, CMD_FORCE_ENCRYPTION);
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
}
// Test: /force-encryption on enables encryption.
void
test_cmd_force_encryption_toggles_on(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, FALSE);
char* args[] = { "on", NULL };
expect_cons_show("Forces encryption enabled.");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
assert_true(prefs_get_boolean(PREF_FORCE_ENCRYPTION));
}
// Test: /force-encryption off disables encryption.
void
test_cmd_force_encryption_toggles_off(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, TRUE);
char* args[] = { "off", NULL };
expect_cons_show("Forces encryption disabled.");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
assert_false(prefs_get_boolean(PREF_FORCE_ENCRYPTION));
}
// Test: /force-encryption off when already off.
void
test_cmd_force_encryption_toggles_off_already(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, FALSE);
char* args[] = { "off", NULL };
expect_cons_show("Forces encryption is already disabled.");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
assert_false(prefs_get_boolean(PREF_FORCE_ENCRYPTION));
}
// Test: /force-encryption on when already on.
void
test_cmd_force_encryption_toggles_on_already(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, TRUE);
char* args[] = { "on", NULL };
expect_cons_show("Forces encryption is already enabled.");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
assert_true(prefs_get_boolean(PREF_FORCE_ENCRYPTION));
}
// Test: /force-encryption policy block sets block mode.
void
test_cmd_force_encryption_sets_policy_block(void** state)
{
char* args[] = { "policy", "block", NULL };
expect_cons_show("Force encryption policy has been set to \"block\".");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
auto_gchar gchar* pref_force_enc_mode = prefs_get_string(PREF_FORCE_ENCRYPTION_MODE);
assert_string_equal(pref_force_enc_mode, "block");
}
// Test: /force-encryption policy resend-to-confirm sets resend mode.
void
test_cmd_force_encryption_sets_policy_resend(void** state)
{
char* args[] = { "policy", "resend-to-confirm", NULL };
expect_cons_show("Force encryption policy has been set to \"resend-to-confirm\".");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
auto_gchar gchar* pref_force_enc_mode = prefs_get_string(PREF_FORCE_ENCRYPTION_MODE);
assert_string_equal(pref_force_enc_mode, "resend-to-confirm");
}
// Test: Unencrypted message blocked when policy is block.
void
test_allow_unencrypted_message_blocks(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, TRUE);
prefs_set_string(PREF_FORCE_ENCRYPTION_MODE, "block");
ProfChatWin win = {};
char* msg = "test message";
expect_win_println("Message not sent: encryption required. Enable (omemo/pgp/otr) encryption or /force-encryption off.");
assert_false(allow_unencrypted_message(&win, msg));
}
// Test: Unencrypted message prompts confirmation, then allows on second attempt.
void
test_allow_unencrypted_message_confirms_on_second_attempt(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, TRUE);
prefs_set_string(PREF_FORCE_ENCRYPTION_MODE, "resend-to-confirm");
ProfChatWin win = { .last_attempted_msg_hash = 0 };
char* msg = "test message";
expect_win_println("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.");
assert_false(allow_unencrypted_message(&win, msg));
assert_int_not_equal(win.last_attempted_msg_hash, 0);
assert_true(allow_unencrypted_message(&win, msg));
assert_int_equal(win.last_attempted_msg_hash, 0); // ensure that value resets since we don't want to approve the same message twice
}
// Test: Invalid policy argument fails gracefully.
void
test_cmd_force_encryption_invalid_policy(void** state)
{
char* args[] = { "policy", "invalid", NULL };
expect_cons_show_error("Invalid policy: \"invalid\". See '/help force-encryption' for details.");
assert_true(cmd_force_encryption(NULL, CMD_FORCE_ENCRYPTION, args));
}
// Test: Invalid encryption mode blocks message.
void
test_allow_unencrypted_message_invalid_mode(void** state)
{
prefs_set_boolean(PREF_FORCE_ENCRYPTION, TRUE);
prefs_set_string(PREF_FORCE_ENCRYPTION_MODE, "invalid");
ProfChatWin win = { .last_attempted_msg_hash = 0 };
char* msg = "test message";
expect_win_println("Message not sent: invalid encryption mode (invalid). Use '/force-encryption policy resend-to-confirm'.");
assert_false(allow_unencrypted_message(&win, msg));
assert_int_equal(win.last_attempted_msg_hash, 0);
}

View File

@@ -0,0 +1,16 @@
#ifndef TEST_FORCED_ENCRYPTION_H
#define TEST_FORCED_ENCRYPTION_H
void test_cmd_force_encryption_no_args_bad_cmd(void** state);
void test_cmd_force_encryption_toggles_on(void** state);
void test_cmd_force_encryption_toggles_off(void** state);
void test_cmd_force_encryption_toggles_off_already(void** state);
void test_cmd_force_encryption_toggles_on_already(void** state);
void test_cmd_force_encryption_sets_policy_block(void** state);
void test_cmd_force_encryption_sets_policy_resend(void** state);
void test_allow_unencrypted_message_blocks(void** state);
void test_allow_unencrypted_message_confirms_on_second_attempt(void** state);
void test_cmd_force_encryption_invalid_policy(void** state);
void test_allow_unencrypted_message_invalid_mode(void** state);
#endif // TEST_FORCED_ENCRYPTION_H

View File

@@ -675,6 +675,11 @@ inp_readline(void)
return NULL;
}
void
inp_set_line(const char* const new_line)
{
}
void
inp_nonblocking(gboolean reset)
{
@@ -1157,6 +1162,11 @@ cons_privacy_setting(void)
{
}
void
cons_encryption_setting(void)
{
}
void
cons_show_bookmarks_ignore(gchar** list, gsize len)
{

View File

@@ -24,6 +24,7 @@
#include "test_cmd_presence.h"
#include "test_cmd_otr.h"
#include "test_cmd_pgp.h"
#include "test_forced_encryption.h"
#include "test_jid.h"
#include "test_parser.h"
#include "test_roster_list.h"
@@ -385,6 +386,7 @@ main(int argc, char* argv[])
cmocka_unit_test(cmd_sub_shows_message_when_not_connected),
cmocka_unit_test(cmd_sub_shows_usage_when_no_arg),
cmocka_unit_test(test_cmd_sub_malformed_jid),
cmocka_unit_test(contact_in_group),
cmocka_unit_test(contact_not_in_group),
@@ -644,6 +646,19 @@ main(int argc, char* argv[])
cmocka_unit_test(does_not_add_duplicate_feature),
cmocka_unit_test(removes_plugin_features),
cmocka_unit_test(does_not_remove_feature_when_more_than_one_reference),
// Forced encryption
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_no_args_bad_cmd, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_toggles_on, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_toggles_off, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_toggles_off_already, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_toggles_on_already, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_sets_policy_block, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_sets_policy_resend, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_allow_unencrypted_message_blocks, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_allow_unencrypted_message_confirms_on_second_attempt, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_cmd_force_encryption_invalid_policy, load_preferences, close_preferences),
cmocka_unit_test_setup_teardown(test_allow_unencrypted_message_invalid_mode, load_preferences, close_preferences),
};
return cmocka_run_group_tests(all_tests, NULL, NULL);
}