Compare commits

..

1 Commits

Author SHA1 Message Date
1c0f39db54 ref(theme): ensure clean reinitialization and explicitly initialize static vars
Some checks failed
CI / Check spelling (pull_request) Successful in 18s
CI / Check coding style (pull_request) Failing after 28s
CI / Linux (debian) (pull_request) Successful in 9m39s
CI / Linux (ubuntu) (pull_request) Successful in 9m59s
CI / Linux (fedora) (pull_request) Successful in 10m38s
CI / Linux (arch) (pull_request) Successful in 16m6s
Improves code robustness by calling _theme_close() in theme_init() when the
theme was already initialized, preventing potential memory leaks or
inconsistent state.

Closes #4
2025-06-24 15:53:10 +02:00
31 changed files with 223 additions and 523 deletions

3
.gitignore vendored
View File

@@ -44,9 +44,6 @@ src/gitversion.h.in
src/stamp-h1
src/plugins/profapi.lo
# clangd
.cache/
# out-of-tree build folders
build*/

View File

@@ -1865,7 +1865,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = YES
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of

View File

@@ -1 +1 @@
sphinx-apidoc -f -o . src && make xml
sphinx-apidoc -f -o . src && make html

View File

@@ -5,7 +5,7 @@
#
# This file is a combination of our own rules and
# * the glib2 suppressions file that usually can be found at /usr/share/glib-2.0/valgrind/glib.supp
# * python suppressions file from https://github.com/python/cpython/blob/main/Misc/valgrind-python.supp
# * python suppressions file from http://svn.python.org/projects/python/trunk/Misc/valgrind-python.supp
#
{
@@ -1557,10 +1557,6 @@
...
fun:xdg_mime_init
}
# Python Valgrind .supp start
# taken from https://github.com/python/cpython/blob/main/Misc/valgrind-python.supp
#
# This is a valgrind suppression file that should be used when using valgrind.
#
@@ -1658,49 +1654,6 @@
fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
}
#
# Leaks: dlopen() called without dlclose()
#
{
dlopen() called without dlclose()
Memcheck:Leak
fun:malloc
fun:malloc
fun:strdup
fun:_dl_load_cache_lookup
}
{
dlopen() called without dlclose()
Memcheck:Leak
fun:malloc
fun:malloc
fun:strdup
fun:_dl_map_object
}
{
dlopen() called without dlclose()
Memcheck:Leak
fun:malloc
fun:*
fun:_dl_new_object
}
{
dlopen() called without dlclose()
Memcheck:Leak
fun:calloc
fun:*
fun:_dl_new_object
}
{
dlopen() called without dlclose()
Memcheck:Leak
fun:calloc
fun:*
fun:_dl_check_map_versions
}
#
# Non-python specific leaks
#
@@ -2095,10 +2048,6 @@
fun:PyUnicode_FSConverter
}
# Python Valgrind .supp end
# Actual GTK things
{
GtkWidgetClass action GPtrArray

View File

@@ -1715,15 +1715,8 @@ cmd_help(ProfWin* window, const char* const command, gchar** args)
gboolean
cmd_about(ProfWin* window, const char* const command, gchar** args)
{
ProfWin* console = wins_get_console();
cons_show("");
cons_about();
win_println(console, THEME_DEFAULT, "-", "Licensed under GNU GPL v3.");
win_println(console, THEME_DEFAULT, "-", "CProof is an independent unaffiliated fork of Profanity.");
win_println(console, THEME_DEFAULT, "-", "Profanity: Copyright (C) 20122019 James Booth, 20192025 Michael Vetter");
win_println(console, THEME_DEFAULT, "-", "Copyright (C) 2025 CProof Developers");
win_println(console, THEME_DEFAULT, "-", "");
return TRUE;
}
@@ -5240,43 +5233,54 @@ cmd_console(ProfWin* window, const char* const command, gchar** args)
gboolean
cmd_presence(ProfWin* window, const char* const command, gchar** args)
{
if (strcmp(args[0], "console") != 0 && strcmp(args[0], "chat") != 0 && strcmp(args[0], "room") != 0 && strcmp(args[0], "titlebar") != 0) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (strcmp(args[0], "titlebar") == 0) {
_cmd_set_boolean_preference(args[1], "Contact presence", PREF_PRESENCE);
return TRUE;
}
const struct presence_preferences
{
preference_t pref;
const char *name, *description;
} presence_prefs[] = {
{ .pref = PREF_STATUSES_CONSOLE, .name = "console", .description = "the console" },
{ .pref = PREF_STATUSES_CHAT, .name = "chat", .description = "chat windows" },
{ .pref = PREF_STATUSES_MUC, .name = "room", .description = "chat room windows" },
};
size_t n = 0;
for (; n < ARRAY_SIZE(presence_prefs); ++n) {
const struct presence_preferences* pp = &presence_prefs[n];
if (g_strcmp0(args[0], pp->name) != 0)
continue;
if (strcmp(args[1], "all") == 0) {
cons_show("All presence updates will appear in %s.", pp->description);
} else if (strcmp(args[1], "online") == 0) {
cons_show("Only %s presence updates will appear in %s.",
pp->pref == PREF_STATUSES_MUC ? "join/leave" : "online/offline",
pp->description);
} else if (strcmp(args[1], "none") == 0) {
cons_show("Presence updates will not appear in %s.", pp->description);
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
prefs_set_string(pp->pref, args[1]);
break;
}
if (n == ARRAY_SIZE(presence_prefs)) {
if (strcmp(args[1], "all") != 0 && strcmp(args[1], "online") != 0 && strcmp(args[1], "none") != 0) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (strcmp(args[0], "console") == 0) {
prefs_set_string(PREF_STATUSES_CONSOLE, args[1]);
if (strcmp(args[1], "all") == 0) {
cons_show("All presence updates will appear in the console.");
} else if (strcmp(args[1], "online") == 0) {
cons_show("Only online/offline presence updates will appear in the console.");
} else {
cons_show("Presence updates will not appear in the console.");
}
}
if (strcmp(args[0], "chat") == 0) {
prefs_set_string(PREF_STATUSES_CHAT, args[1]);
if (strcmp(args[1], "all") == 0) {
cons_show("All presence updates will appear in chat windows.");
} else if (strcmp(args[1], "online") == 0) {
cons_show("Only online/offline presence updates will appear in chat windows.");
} else {
cons_show("Presence updates will not appear in chat windows.");
}
}
if (strcmp(args[0], "room") == 0) {
prefs_set_string(PREF_STATUSES_MUC, args[1]);
if (strcmp(args[1], "all") == 0) {
cons_show("All presence updates will appear in chat room windows.");
} else if (strcmp(args[1], "online") == 0) {
cons_show("Only join/leave presence updates will appear in chat room windows.");
} else {
cons_show("Presence updates will not appear in chat room windows.");
}
}
return TRUE;
}
@@ -5310,8 +5314,7 @@ cmd_time(ProfWin* window, const char* const command, gchar** args)
};
gboolean redraw = FALSE;
gboolean set_all = g_strcmp0(args[0], "all") == 0;
size_t n = 0;
for (; n < ARRAY_SIZE(time_prefs); ++n) {
for (size_t n = 0; n < ARRAY_SIZE(time_prefs); ++n) {
if (!set_all && g_strcmp0(args[0], time_prefs[n].name) != 0)
continue;
const struct time_preferences* tp = &time_prefs[n];
@@ -5352,9 +5355,6 @@ cmd_time(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
}
if (!set_all && n == ARRAY_SIZE(time_prefs)) {
cons_bad_cmd_usage(command);
}
if (redraw)
ui_redraw();
return TRUE;
@@ -9527,7 +9527,16 @@ cmd_change_password(ProfWin* window, const char* const command, gchar** args)
gboolean
cmd_editor(ProfWin* window, const char* const command, gchar** args)
{
get_message_from_editor_async(NULL);
auto_gchar gchar* message = NULL;
if (get_message_from_editor(NULL, &message)) {
return TRUE;
}
rl_insert_text(message);
ui_resize();
rl_point = rl_end;
rl_forced_update_display();
return TRUE;
}

View File

@@ -171,8 +171,6 @@ typedef enum {
RESOURCE_XA
} resource_presence_t;
extern gboolean background_mode;
gboolean string_to_verbosity(const char* cmd, int* verbosity, gchar** err_msg);
gboolean create_dir(const char* name);

View File

@@ -86,9 +86,9 @@ _theme_close(void)
void
theme_init(const char* const theme_name)
{
if (defaults || theme_loc || theme) {
if(defaults || theme_loc || theme) {
log_warning("theme_init(): Detected non-NULL state (theme=%p, theme_loc=%p, defaults=%p). Cleaning up previous initialization.",
theme, theme_loc, defaults);
theme, theme_loc, defaults);
_theme_close();
}

View File

@@ -222,7 +222,6 @@ out:
void
log_database_close(void)
{
log_debug("log_database_close() called");
if (g_chatlog_database) {
sqlite3_close(g_chatlog_database);
sqlite3_shutdown();
@@ -319,25 +318,19 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
// Query previous chats, constraints start_time and end_time. If end_time is
// null the current time is used. from_start gets first few messages if true
// otherwise the last ones. Flip flips the order of the results
db_history_result_t
log_database_get_previous_chat(const gchar* const contact_barejid, const gchar* start_time, const gchar* end_time, gboolean from_start, gboolean flip, GSList** result)
GSList*
log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip)
{
if (!g_chatlog_database) {
log_warning("log_database_get_previous_chat() called but db is not initialized");
return DB_RESPONSE_ERROR;
}
sqlite3_stmt* stmt = NULL;
const Jid* myjid = connection_get_jid();
if (!myjid->str) {
log_warning("log_database_get_previous_chat() called but no connection detected.");
return DB_RESPONSE_ERROR;
}
if (!myjid->str)
return NULL;
// Flip order when querying older pages
gchar* sort1 = from_start ? "ASC" : "DESC";
gchar* sort2 = !flip ? "ASC" : "DESC";
GDateTime* now = g_date_time_new_now_local();
auto_gchar gchar* end_date_fmt = end_time ? g_strdup(end_time) : g_date_time_format_iso8601(now);
auto_gchar gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now);
auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM ("
"SELECT COALESCE(B.`message`, A.`message`) AS message, "
"A.`timestamp`, A.`from_jid`, A.`from_resource`, A.`to_jid`, A.`to_resource`, A.`type`, A.`encryption`, A.`stanza_id` FROM `ChatLogs` AS A "
@@ -353,16 +346,18 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const gchar*
g_date_time_unref(now);
if (!query) {
log_error("Could not allocate memory.");
return DB_RESPONSE_ERROR;
log_error("Could not allocate memory");
return NULL;
}
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
log_error("SQLite error in log_database_get_previous_chat(): (error code: %d) %s", rc, sqlite3_errmsg(g_chatlog_database));
return DB_RESPONSE_ERROR;
log_error("SQLite error in log_database_get_previous_chat(): %s", sqlite3_errmsg(g_chatlog_database));
return NULL;
}
GSList* history = NULL;
while (sqlite3_step(stmt) == SQLITE_ROW) {
char* message = (char*)sqlite3_column_text(stmt, 0);
char* date = (char*)sqlite3_column_text(stmt, 1);
@@ -383,11 +378,11 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const gchar*
msg->type = _get_message_type_type(type);
msg->enc = _get_message_enc_type(encryption);
*result = g_slist_append(*result, msg);
history = g_slist_append(history, msg);
}
sqlite3_finalize(stmt);
return g_slist_length(*result) != 0 ? DB_RESPONSE_SUCCESS : DB_RESPONSE_EMPTY;
return history;
}
static const char*

View File

@@ -40,20 +40,14 @@
#include "config/account.h"
#include "xmpp/xmpp.h"
#define MESSAGES_TO_RETRIEVE 100
typedef enum {
DB_RESPONSE_ERROR,
DB_RESPONSE_EMPTY,
DB_RESPONSE_SUCCESS
} db_history_result_t;
#define MESSAGES_TO_RETRIEVE 10
gboolean log_database_init(ProfAccount* account);
void log_database_add_incoming(ProfMessage* message);
void log_database_add_outgoing_chat(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
void log_database_add_outgoing_muc(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
void log_database_add_outgoing_muc_pm(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
db_history_result_t log_database_get_previous_chat(const gchar* const contact_barejid, const gchar* start_time, const gchar* end_time, gboolean from_start, gboolean flip, GSList** result);
GSList* log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip);
ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last);
void log_database_close(void);

View File

@@ -664,8 +664,7 @@ p_gpg_decrypt(const char* const cipher)
}
gpgme_key_unref(key);
} else if (error) {
// Emails are used only to fetch PGP recipients for debug logging. If something went wrong here, it's not worthy of warning.
log_debug("GPGME error on gpgme_get_key(): %s", gpgme_strerror(error));
log_warning("GPGME error: %s", gpgme_strerror(error));
}
if (recipient->next) {

View File

@@ -900,8 +900,6 @@ _python_type_error(ProfPlugin* plugin, char* hook, char* type)
g_string_free(err_msg, TRUE);
}
// Converts a Python string or None result to a C string and decreases the Python object's reference count.
// If the result is NULL, or not a string/unicode/bytes/None, logs an error and returns NULL.
static char*
_handle_string_or_none_result(ProfPlugin* plugin, PyObject* result, char* hook)
{
@@ -912,16 +910,18 @@ _handle_string_or_none_result(ProfPlugin* plugin, PyObject* result, char* hook)
}
#ifdef PY_IS_PYTHON3
if (result != Py_None && !PyUnicode_Check(result) && !PyBytes_Check(result)) {
#else
if (result != Py_None && !PyUnicode_Check(result) && !PyString_Check(result)) {
#endif
Py_XDECREF(result);
allow_python_threads();
_python_type_error(plugin, hook, "string, unicode or None");
return NULL;
}
#else
if (result != Py_None && !PyUnicode_Check(result) && !PyString_Check(result)) {
allow_python_threads();
_python_type_error(plugin, hook, "string, unicode or None");
return NULL;
}
#endif
char* result_str = python_str_or_unicode_to_string(result);
Py_XDECREF(result);
allow_python_threads();
return result_str;
}

View File

@@ -59,7 +59,6 @@
#include "config/tlscerts.h"
#include "config/scripts.h"
#include "command/cmd_defs.h"
#include "tools/editor.h"
#include "plugins/plugins.h"
#include "event/client_events.h"
#include "ui/ui.h"
@@ -94,9 +93,6 @@ static void _connect_default(const char* const account);
pthread_mutex_t lock;
static gboolean force_quit = FALSE;
// main.c (prof_run section)
gboolean background_mode = FALSE;
void
prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_file, gchar* theme_name, gchar** commands)
{
@@ -122,44 +118,42 @@ prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_f
log_stderr_handler();
session_check_autoaway();
if (!background_mode) {
line = commands ? *commands : inp_readline();
if (commands && line && memcmp(line, "/sleep", 6) == 0) {
if (!g_timer_is_active(waittimer)) {
gchar* err_msg;
if (strtoi_range(line + 7, &waittime, 0, 300, &err_msg)) {
g_timer_start(waittimer);
/* Increase the minimal runtime by the waiting time
* so we can be sure there's runtime left after executing
* the last command.
*/
min_runtime += waittime;
} else {
log_error(err_msg);
g_free(err_msg);
commands = NULL;
}
} else if (g_timer_elapsed(waittimer, NULL) >= waittime) {
g_timer_stop(waittimer);
commands++;
if (!(*commands))
commands = NULL;
}
cont = TRUE;
} else if (line) {
ProfWin* window = wins_get_current();
cont = cmd_process_input(window, line);
if (commands) {
commands++;
if (!(*commands))
commands = NULL;
line = commands ? *commands : inp_readline();
if (commands && line && memcmp(line, "/sleep", 6) == 0) {
if (!g_timer_is_active(waittimer)) {
gchar* err_msg;
if (strtoi_range(line + 7, &waittime, 0, 300, &err_msg)) {
g_timer_start(waittimer);
/* Increase the minimal runtime by the waiting time
* so we can be sure there's runtime left after executing
* the last command.
*/
min_runtime += waittime;
} else {
free(line);
line = NULL;
log_error(err_msg);
g_free(err_msg);
commands = NULL;
}
} else {
cont = TRUE;
} else if (g_timer_elapsed(waittimer, NULL) >= waittime) {
g_timer_stop(waittimer);
commands++;
if (!(*commands))
commands = NULL;
}
cont = TRUE;
} else if (line) {
ProfWin* window = wins_get_current();
cont = cmd_process_input(window, line);
if (commands) {
commands++;
if (!(*commands))
commands = NULL;
} else {
free(line);
line = NULL;
}
} else {
cont = TRUE;
}
#ifdef HAVE_LIBOTR
@@ -169,12 +163,7 @@ prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_f
notify_remind();
session_process_events();
iq_autoping_check();
editor_process(wins_get_current());
if (!background_mode) {
ui_update();
}
ui_update();
#ifdef HAVE_GTK
tray_update();
#endif

View File

@@ -140,9 +140,6 @@ aesgcm_file_get(void* userdata)
"Downloading '%s' failed: Failed to decrypt "
"file (%s).",
https_url, gcry_strerror(crypt_res));
} else {
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id,
"Decrypted file saved to '%s'", aesgcm_dl->filename);
}
if (aesgcm_dl->cmd_template != NULL) {

View File

@@ -38,200 +38,15 @@
#include <fcntl.h>
#include <glib.h>
#include <errno.h>
#include <stdio.h> // necessary for readline
#include <readline/readline.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include "config/files.h"
#include "config/preferences.h"
#include "editor.h"
#include "log.h"
#include "common.h"
#include "ncurses.h"
#include "ui/ui.h"
#include "xmpp/xmpp.h"
EditorTask editor_task = { 0, FALSE, NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL };
void*
editor_thread(void* arg)
{
EditorTask* task = (EditorTask*)arg;
auto_gcharv gchar** argv = g_strsplit(task->editor_cmd, " ", 0);
pid_t pid = fork();
if (pid == 0) {
execvp(argv[0], argv);
log_error("[Editor] Failed to exec %s", argv[0]);
_exit(EXIT_FAILURE);
}
pthread_mutex_lock(&task->mutex);
if (pid > 0) {
task->pid = pid;
int status;
// Unlock mutex to avoid deadlock on the main loop
pthread_mutex_unlock(&task->mutex);
waitpid(pid, &status, 0);
pthread_mutex_lock(&task->mutex);
gsize length;
if (!g_file_get_contents(task->filename, &task->result, &length, &task->error)) {
log_error("[Editor] could not read from %s: %s", task->filename,
task->error ? task->error->message : "No GLib error given");
task->result = g_strdup(""); // Ensure valid result
} else {
g_strchomp(task->result);
if (remove(task->filename) != 0) {
log_error("[Editor] error during file deletion of %s", task->filename);
} else {
log_debug("[Editor] deleted file: %s", task->filename);
}
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
task->error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Editor process failed with status %d", WEXITSTATUS(status));
}
} else {
task->error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED, "Fork failed: %s", strerror(errno));
task->result = g_strdup(""); // Ensure valid result
}
task->active = FALSE;
pthread_mutex_unlock(&task->mutex);
return NULL;
}
// Returns true if an error occurred
gboolean
get_message_from_editor_async(gchar* message)
{
// Check if editor is already running
pthread_mutex_lock(&editor_task.mutex);
if (editor_task.active) {
pthread_mutex_unlock(&editor_task.mutex);
log_error("[Editor] Editor already running");
return TRUE;
}
pthread_mutex_unlock(&editor_task.mutex);
// Create temporary file
auto_gchar gchar* filename = NULL;
GError* glib_error = NULL;
const char* jid = connection_get_barejid();
if (jid) {
filename = files_file_in_account_data_path(DIR_EDITOR, jid, "compose.md");
} else {
log_debug("[Editor] could not get JID");
auto_gchar gchar* data_dir = files_get_data_path(DIR_EDITOR);
if (!create_dir(data_dir)) {
log_error("[Editor] could not create directory %s", data_dir);
return TRUE;
}
filename = g_strdup_printf("%s/compose.md", data_dir);
}
if (!filename) {
log_error("[Editor] something went wrong while creating compose file");
return TRUE;
}
// Write message to file
gsize messagelen = message ? strlen(message) : 0;
if (!g_file_set_contents(filename, message ? message : "", messagelen, &glib_error)) {
log_error("[Editor] could not write to %s: %s", filename,
glib_error ? glib_error->message : "No GLib error given");
if (glib_error) {
g_error_free(glib_error);
}
return TRUE;
}
// Prepare editor command
auto_gchar gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
auto_gchar gchar* editor_with_filename = g_strdup_printf("%s %s", editor, filename);
// Suspend NCurses
def_prog_mode();
endwin();
// Initialize thread task
pthread_mutex_lock(&editor_task.mutex);
editor_task.active = TRUE;
editor_task.result = NULL;
editor_task.error = NULL;
g_free(editor_task.filename);
g_free(editor_task.editor_cmd);
editor_task.filename = g_strdup(filename);
editor_task.editor_cmd = g_strdup(editor_with_filename);
pthread_mutex_unlock(&editor_task.mutex);
// Set background mode
background_mode = TRUE;
log_debug("[Editor] Entering background mode");
// Start editor thread
if (pthread_create(&editor_task.thread, NULL, editor_thread, &editor_task) != 0) {
pthread_mutex_lock(&editor_task.mutex);
editor_task.active = FALSE;
editor_task.error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Failed to create editor thread: %s", strerror(errno));
g_free(editor_task.filename);
g_free(editor_task.editor_cmd);
editor_task.filename = NULL;
editor_task.editor_cmd = NULL;
pthread_mutex_unlock(&editor_task.mutex);
background_mode = FALSE;
log_debug("[Editor] Exiting background mode");
reset_prog_mode();
doupdate();
log_error("[Editor] Failed to create editor thread");
return TRUE;
}
return FALSE;
}
void
editor_process(ProfWin* window)
{
pthread_mutex_lock(&editor_task.mutex);
if (editor_task.active) {
pthread_mutex_unlock(&editor_task.mutex);
return;
}
if (!editor_task.result) {
pthread_mutex_unlock(&editor_task.mutex);
return;
}
g_strchomp(editor_task.result);
rl_replace_line(editor_task.result, 0);
rl_point = rl_end;
rl_forced_update_display();
reset_prog_mode();
doupdate();
ui_resize();
g_free(editor_task.result);
g_free(editor_task.filename);
g_free(editor_task.editor_cmd);
editor_task.result = NULL;
editor_task.filename = NULL;
editor_task.editor_cmd = NULL;
if (editor_task.error) {
log_error("[Editor] %s", editor_task.error->message);
g_error_free(editor_task.error);
editor_task.error = NULL;
}
pthread_mutex_unlock(&editor_task.mutex);
background_mode = FALSE;
log_debug("[Editor] Exiting background mode");
}
// Deprecated synchronous editor call. Returns a message as returned_message.
// Please avoid using it, since it blocks execution of the message handling and other important functionality until the editor is closed.
// Returns true if an error occurred
gboolean
get_message_from_editor(gchar* message, gchar** returned_message)

View File

@@ -37,24 +37,8 @@
#ifndef TOOLS_EDITOR_H
#define TOOLS_EDITOR_H
#include "ui/win_types.h"
#include <glib.h>
typedef struct
{
pid_t pid; // Editor process PID
gboolean active; // Thread running
gchar* result; // Edited message
GError* error; // Error if any
pthread_mutex_t mutex; // Protect access
pthread_t thread; // Editor thread
gchar* filename; // Temporary file path
gchar* editor_cmd; // Editor command with filename
} EditorTask;
extern EditorTask editor_task;
gboolean get_message_from_editor(gchar* message, gchar** returned_message);
gboolean get_message_from_editor_async(gchar* message);
void editor_process(ProfWin* window);
#endif

View File

@@ -109,7 +109,6 @@ _buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* t
buffer->lines += e->_lines;
while (g_slist_length(buffer->entries) >= MAX_BUFFER_SIZE) {
// Delete message from the opposite size to free buffer
GSList* buffer_entry_to_delete = append ? buffer->entries : g_slist_last(buffer->entries);
ProfBuffEntry* entry_to_delete = (ProfBuffEntry*)buffer_entry_to_delete->data;
buffer->lines -= entry_to_delete->_lines;
@@ -118,13 +117,7 @@ _buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* t
}
if (from_jid && y_end_pos == y_start_pos) {
log_warning("Ncurses Overflow! From: %s, position: %d, ID: %s, append: %s, used message buffer size: %d, message buffer size: %d, rendered lines buffer size: %d",
from_jid, y_start_pos, id, append ? "TRUE" : "FALSE", g_slist_length(buffer->entries), MAX_BUFFER_SIZE, PAD_SIZE);
// At this point we have a message that caused overflow of the render buffer.
// Ideally, we want to clean other messages to rerender everything properly. To do so,
// first we need to determine the size of the message that we are trying to display,
// then we need to print the message.
// However, _buffer_add is too late in the code to do this, therefore it has to be done in the win_print_old_history.
log_warning("Ncurses Overflow! From: %s, pos: %d, ID: %s, message: %s", from_jid, y_end_pos, id, message);
}
buffer->entries = append ? g_slist_append(buffer->entries, e) : g_slist_prepend(buffer->entries, e);

View File

@@ -40,7 +40,6 @@
#include <stdlib.h>
#include <assert.h>
#include "glib.h"
#include "xmpp/chat_session.h"
#include "window_list.h"
#include "xmpp/roster_list.h"
@@ -562,8 +561,7 @@ static void
_chatwin_history(ProfChatWin* chatwin, const char* const contact_barejid)
{
if (!chatwin->history_shown) {
GSList* history = NULL;
log_database_get_previous_chat(contact_barejid, NULL, NULL, FALSE, FALSE, &history);
GSList* history = log_database_get_previous_chat(contact_barejid, NULL, NULL, FALSE, FALSE);
GSList* curr = history;
while (curr) {
@@ -585,17 +583,15 @@ _chatwin_history(ProfChatWin* chatwin, const char* const contact_barejid)
// Print history starting from start_time to end_time if end_time is null the
// first entry's timestamp in the buffer is used. Flip true to prepend to buffer.
// Timestamps should be in iso8601
db_history_result_t
chatwin_db_history(ProfChatWin* chatwin, const gchar* start_time, const gchar* end_time, gboolean flip)
gboolean
chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip)
{
auto_gchar gchar* _end_time = NULL;
if (!end_time && buffer_size(((ProfWin*)chatwin)->layout->buffer) > 0) {
_end_time = g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)chatwin)->layout->buffer, 0)->time);
end_time = _end_time;
if (!end_time) {
end_time = buffer_size(((ProfWin*)chatwin)->layout->buffer) == 0 ? NULL : g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)chatwin)->layout->buffer, 0)->time);
}
GSList* history = NULL;
db_history_result_t result = log_database_get_previous_chat(chatwin->barejid, start_time, end_time, !flip, flip, &history);
GSList* history = log_database_get_previous_chat(chatwin->barejid, start_time, end_time, !flip, flip);
gboolean has_items = g_slist_length(history) != 0;
GSList* curr = history;
while (curr) {
@@ -614,12 +610,9 @@ chatwin_db_history(ProfChatWin* chatwin, const gchar* start_time, const gchar* e
}
g_slist_free_full(history, (GDestroyNotify)message_free);
// TODO: Potential optimization
// if (flip)
// since adding messages to history without overflowing buffer does not require a win_redraw
win_redraw((ProfWin*)chatwin);
return result;
return has_items;
}
static void

View File

@@ -387,9 +387,16 @@ cons_about(void)
_cons_splash_logo();
} else {
auto_gchar gchar* prof_version = prof_get_version();
win_println(console, THEME_DEFAULT, "-", "Welcome to CProof, version %s", prof_version);
win_println(console, THEME_DEFAULT, "-", "Welcome to Profanity, version %s", prof_version);
}
win_println(console, THEME_DEFAULT, "-", "Copyright (C) 2012 - 2019 James Booth <boothj5web@gmail.com>.");
win_println(console, THEME_DEFAULT, "-", "Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>.");
win_println(console, THEME_DEFAULT, "-", "License GPLv3+: GNU GPL version 3 or later <https://www.gnu.org/licenses/gpl.html>");
win_println(console, THEME_DEFAULT, "-", "");
win_println(console, THEME_DEFAULT, "-", "This is free software; you are free to change and redistribute it.");
win_println(console, THEME_DEFAULT, "-", "There is NO WARRANTY, to the extent permitted by law.");
win_println(console, THEME_DEFAULT, "-", "");
win_println(console, THEME_DEFAULT, "-", "Type '/help' to show complete help.");
win_println(console, THEME_DEFAULT, "-", "");
@@ -477,7 +484,7 @@ cons_show_wins(gboolean unread)
}
curr = g_slist_next(curr);
}
g_slist_free_full(window_strings, g_free);
g_slist_free_full(window_strings, free);
cons_alert(NULL);
}
@@ -498,7 +505,7 @@ cons_show_wins_attention()
}
curr = g_slist_next(curr);
}
g_slist_free_full(window_strings, g_free);
g_slist_free_full(window_strings, free);
cons_alert(NULL);
}
@@ -2697,12 +2704,13 @@ _cons_splash_logo(void)
ProfWin* console = wins_get_console();
win_println(console, THEME_DEFAULT, "-", "Welcome to");
win_println(console, THEME_SPLASH, "-", " _____ _____ __ ");
win_println(console, THEME_SPLASH, "-", " / ____| __ \\ / _|");
win_println(console, THEME_SPLASH, "-", " | | | |__) | __ ___ ___ | |_ ");
win_println(console, THEME_SPLASH, "-", " | | | ___/ '__/ _ \\ / _ \\| _|");
win_println(console, THEME_SPLASH, "-", " | |____| | | | | (_) | (_) | | ");
win_println(console, THEME_SPLASH, "-", " \\_____|_| |_| \\___/ \\___/|_| ");
win_println(console, THEME_SPLASH, "-", " ___ _ ");
win_println(console, THEME_SPLASH, "-", " / __) (_)_ ");
win_println(console, THEME_SPLASH, "-", " ____ ___ ___ | |__ ____ ____ _| |_ _ _ ");
win_println(console, THEME_SPLASH, "-", "| _ \\ / __) _ \\| __) _ | _ \\| | _) | | |");
win_println(console, THEME_SPLASH, "-", "| | ) | | | (_) | | | ( | | | | | | |_| |_| |");
win_println(console, THEME_SPLASH, "-", "| ||_/|_| \\___/|_| \\_||_|_| |_|_|\\___)__ |");
win_println(console, THEME_SPLASH, "-", "|_| (____/ ");
win_println(console, THEME_SPLASH, "-", "");
auto_gchar gchar* prof_version = prof_get_version();

View File

@@ -974,7 +974,14 @@ _inp_rl_send_to_editor(int count, int key)
auto_gchar gchar* message = NULL;
get_message_from_editor_async(rl_line_buffer);
if (get_message_from_editor(rl_line_buffer, &message)) {
return 0;
}
rl_replace_line(message, 0);
ui_resize();
rl_point = rl_end;
rl_forced_update_display();
return 0;
}

View File

@@ -43,7 +43,6 @@
#include "config/account.h"
#include "config/preferences.h"
#include "command/cmd_funcs.h"
#include "database.h"
#include "ui/win_types.h"
#include "xmpp/message.h"
#include "xmpp/muc.h"
@@ -146,7 +145,7 @@ void chatwin_set_incoming_char(ProfChatWin* chatwin, const char* const ch);
void chatwin_unset_incoming_char(ProfChatWin* chatwin);
void chatwin_set_outgoing_char(ProfChatWin* chatwin, const char* const ch);
void chatwin_unset_outgoing_char(ProfChatWin* chatwin);
db_history_result_t chatwin_db_history(ProfChatWin* chatwin, const gchar* start_time, const gchar* end_time, gboolean flip);
gboolean chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip);
// MUC window
ProfMucWin* mucwin_new(const char* const barejid);

View File

@@ -35,7 +35,6 @@
*/
#include "config.h"
#include "database.h"
#include "ui/window_list.h"
#include <stdlib.h>
@@ -63,8 +62,9 @@
#include "xmpp/xmpp.h"
#include "xmpp/roster_list.h"
static const int PAD_SIZE = 100;
static const char* LOADING_MESSAGE = "Loading older messages…";
static const char* CONS_WIN_TITLE = "CProof. Type /help for help information.";
static const char* CONS_WIN_TITLE = "Profanity. Type /help for help information.";
static const char* XML_WIN_TITLE = "XML Console";
#define CEILING(X) (X - (int)(X) > 0 ? (int)(X + 1) : (int)(X))
@@ -629,68 +629,54 @@ win_free(ProfWin* window)
void
win_page_up(ProfWin* window, int scroll_size)
{
// Page offset from the start of the NCurses PAD (invisible window)
int* page_start = &(window->layout->y_pos);
int page_start_initial = *page_start;
// Size of the visible chat page
int total_rows = getcury(window->layout->win);
int page_space = getmaxy(stdscr) - 4;
if (scroll_size == 0)
scroll_size = page_space;
win_scroll_state_t* scroll_state = &window->scroll_state;
*scroll_state = (*scroll_state == WIN_SCROLL_REACHED_BOTTOM) ? WIN_SCROLL_INNER : *scroll_state;
*page_start -= scroll_size;
gboolean has_scroll_past_buffer = *page_start < 0;
if (has_scroll_past_buffer && window->type == WIN_CHAT) {
if (*page_start == -scroll_size && window->type == WIN_CHAT) {
ProfChatWin* chatwin = (ProfChatWin*)window;
ProfBuffEntry* first_entry = buffer_size(window->layout->buffer) != 0 ? buffer_get_entry(window->layout->buffer, 0) : NULL;
gboolean is_still_fetching_mam = first_entry && (first_entry->theme_item == THEME_ROOMINFO && g_strcmp0(first_entry->message, LOADING_MESSAGE) == 0);
if (!is_still_fetching_mam) {
// WIN_SCROLL_REACHED_TOP means that we reached top of DB and there is no need to refetch data from it
gboolean is_db_offset_jump_needed = false;
// Don't do anything if still fetching mam messages
if (first_entry && !(first_entry->theme_item == THEME_ROOMINFO && g_strcmp0(first_entry->message, LOADING_MESSAGE) == 0)) {
if (*scroll_state != WIN_SCROLL_REACHED_TOP) {
db_history_result_t db_response = chatwin_db_history(chatwin, NULL, NULL, TRUE);
is_db_offset_jump_needed = db_response == DB_RESPONSE_SUCCESS;
*scroll_state = db_response == DB_RESPONSE_EMPTY ? WIN_SCROLL_REACHED_TOP : WIN_SCROLL_INNER;
log_debug("Scroll state after DB history fetch: %s", *scroll_state ? "WIN_SCROLL_REACHED_TOP" : "WIN_SCROLL_INNER");
*scroll_state = !chatwin_db_history(chatwin, NULL, NULL, TRUE) ? WIN_SCROLL_REACHED_TOP : WIN_SCROLL_INNER;
}
if (*scroll_state == WIN_SCROLL_REACHED_TOP) {
*page_start = 0;
if (prefs_get_boolean(PREF_MAM)) {
win_print_loading_history(window);
iq_mam_request_older(chatwin);
}
if (*scroll_state == WIN_SCROLL_REACHED_TOP && prefs_get_boolean(PREF_MAM)) {
win_print_loading_history(window);
iq_mam_request_older(chatwin);
}
// Recalculate scroll offset after loading older messages from the DB.
// *page_start may become negative due to paging up beyond the current buffer.
// To maintain smooth scroll positioning, we adjust it relative to the visual offset
// (y_start_pos) of the first buffer entry *before* DB fetch.
// This ensures a seamless transition as older messages are prepended, regardless
// of how many visual lines they occupy (since messages can vary in height).
// For example, if *page_start == -5 and first_entry->y_start_pos == 40,
// we shift the scroll to 35, preserving the visual context for the user.
if (is_db_offset_jump_needed) {
*page_start = first_entry ? first_entry->y_start_pos + *page_start : 0;
}
int buff_size = buffer_size(window->layout->buffer);
int offset_entry_id = buff_size > 10 ? 10 : buff_size - 1;
int offset = buffer_get_entry(window->layout->buffer, offset_entry_id)->y_end_pos;
*page_start = offset - page_space;
}
}
// went past beginning, show first page
if (*page_start < 0) {
log_debug("win_page_up(): *page_start adjusted to 0 due to negative value (expected if insufficient history messages). Previous value: %d", *page_start);
*page_start = 0;
}
// Update window location only if position has changed
window->layout->paged = 1;
// update only if position has changed
if (page_start_initial != *page_start) {
win_update_virtual(window);
}
// Update scrolling state
int total_rows = getcury(window->layout->win);
window->layout->paged = (total_rows) - *page_start > page_space;
// switch off page if last line and space line visible
if ((total_rows) - *page_start == page_space) {
window->layout->paged = 0;
}
}
void
@@ -710,22 +696,20 @@ win_page_down(ProfWin* window, int scroll_size)
// Scrolled down after reaching the bottom of the page
if ((*page_start > total_rows - page_space || (*page_start == page_space && *page_start >= total_rows)) && window->type == WIN_CHAT) {
int bf_size = buffer_size(window->layout->buffer);
if (bf_size > 0 && *scroll_state != WIN_SCROLL_REACHED_BOTTOM) {
// How many lines are left until end of the screen
int current_offset = total_rows - *page_start;
GDateTime* now = g_date_time_new_now_local();
if (bf_size > 0) {
ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1);
auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time);
auto_gchar gchar* end_date = g_date_time_format_iso8601(now);
db_history_result_t db_response = chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE);
if (db_response == DB_RESPONSE_EMPTY)
*scroll_state = WIN_SCROLL_REACHED_BOTTOM;
// similar to page_up (see explanation there)
// Get offset of the message that was the latest prior to loading messages from the DB
int original_pos = last_entry->y_end_pos;
*page_start = original_pos - current_offset;
GDateTime* now = g_date_time_new_now_local();
gchar* end_date = g_date_time_format_iso8601(now);
g_date_time_unref(now);
if (*scroll_state != WIN_SCROLL_REACHED_BOTTOM && !chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE)) {
*scroll_state = WIN_SCROLL_REACHED_BOTTOM;
} else if (*scroll_state == WIN_SCROLL_REACHED_BOTTOM) {
g_free(end_date);
}
int offset = last_entry->y_end_pos - 1;
*page_start = offset - page_space + scroll_size;
}
}

View File

@@ -56,9 +56,6 @@
#include "xmpp/contact.h"
#include "xmpp/muc.h"
// Max lines per window rendered by NCurses
static const int PAD_SIZE = 10000;
void win_move_to_end(ProfWin* window);
void win_show_status_string(ProfWin* window, const char* const from,
const char* const show, const char* const status,

View File

@@ -258,20 +258,22 @@ GList*
wins_get_private_chats(const char* const roomjid)
{
GList* result = NULL;
auto_gchar gchar* prefix = g_strdup_printf("%s/", roomjid);
GString* prefix = g_string_new(roomjid);
g_string_append(prefix, "/");
GList* curr = values;
while (curr) {
ProfWin* window = curr->data;
if (window->type == WIN_PRIVATE) {
ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
if (roomjid == NULL || g_str_has_prefix(privatewin->fulljid, prefix)) {
if (roomjid == NULL || g_str_has_prefix(privatewin->fulljid, prefix->str)) {
result = g_list_append(result, privatewin);
}
}
curr = g_list_next(curr);
}
g_string_free(prefix, TRUE);
return result;
}
@@ -1065,13 +1067,19 @@ wins_create_summary(gboolean unread)
while (curr) {
ProfWin* window = g_hash_table_lookup(windows, curr->data);
if (!unread || (unread && win_unread(window) > 0)) {
GString* line = g_string_new("");
int ui_index = GPOINTER_TO_INT(curr->data);
auto_gchar gchar* winstring = win_to_string(window);
if (!winstring) {
g_string_free(line, TRUE);
continue;
}
gchar* line = g_strdup_printf("%d: %s", ui_index, winstring);
result = g_slist_append(result, line);
g_string_append_printf(line, "%d: %s", ui_index, winstring);
result = g_slist_append(result, strdup(line->str));
g_string_free(line, TRUE);
}
curr = g_list_next(curr);
@@ -1100,13 +1108,19 @@ wins_create_summary_attention()
has_attention = mucwin->has_attention;
}
if (has_attention) {
GString* line = g_string_new("");
int ui_index = GPOINTER_TO_INT(curr->data);
auto_gchar gchar* winstring = win_to_string(window);
if (!winstring) {
g_string_free(line, TRUE);
continue;
}
gchar* line = g_strdup_printf("%d: %s", ui_index, winstring);
result = g_slist_append(result, line);
g_string_append_printf(line, "%d: %s", ui_index, winstring);
result = g_slist_append(result, strdup(line->str));
g_string_free(line, TRUE);
}
curr = g_list_next(curr);
}

View File

@@ -119,14 +119,6 @@ caps_init(void)
g_hash_table_add(prof_features, strdup(STANZA_NS_LAST_MESSAGE_CORRECTION));
}
if (prefs_get_boolean(PREF_MOOD)) {
g_hash_table_add(prof_features, strdup(STANZA_NS_MOOD_NOTIFY));
}
#ifdef HAVE_OMEMO
g_hash_table_add(prof_features, strdup(XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY));
#endif
my_sha1 = NULL;
}

View File

@@ -2752,22 +2752,6 @@ iq_mam_request(ProfChatWin* win, GDateTime* enddate)
return;
}
// Trim the last 3 characters from the datetime string to remove unwanted suffix (timezone offset)
static void
_truncate_datetime_suffix(gchar* str)
{
if (!str) {
log_warning("_truncate_datetime_suffix(): unexpected null datetime string");
return;
}
size_t len = strlen(str);
if (len >= 3) {
str[len - 3] = '\0';
} else {
log_warning("_truncate_datetime_suffix(): unexpected short datetime string: %s", str);
}
}
static int
_mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
{
@@ -2789,17 +2773,17 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
buffer_remove_entry(window->layout->buffer, 0);
auto_gchar gchar* start_str = NULL;
auto_char char* start_str = NULL;
if (data->start_datestr) {
start_str = g_strdup(data->start_datestr);
start_str = strdup(data->start_datestr);
// Convert to iso8601
_truncate_datetime_suffix(start_str);
start_str[strlen(start_str) - 3] = '\0';
}
auto_gchar gchar* end_str = NULL;
char* end_str = NULL;
if (data->end_datestr) {
end_str = g_strdup(data->end_datestr);
end_str = strdup(data->end_datestr);
// Convert to iso8601
_truncate_datetime_suffix(end_str);
end_str[strlen(end_str) - 3] = '\0';
}
if (is_complete || !data->fetch_next) {

View File

@@ -190,7 +190,7 @@ get_nick_from_full_jid(const char* const full_room_jid)
/*
* get the fulljid, fall back to the barejid
*/
const char*
char*
jid_fulljid_or_barejid(Jid* jid)
{
if (jid->fulljid) {

View File

@@ -63,7 +63,7 @@ gboolean jid_is_valid_room_form(Jid* jid);
char* create_fulljid(const char* const barejid, const char* const resource);
char* get_nick_from_full_jid(const char* const full_room_jid);
const char* jid_fulljid_or_barejid(Jid* jid);
char* jid_fulljid_or_barejid(Jid* jid);
gchar* jid_random_resource(void);
#endif

View File

@@ -621,7 +621,7 @@ _available_handler(xmpp_stanza_t* const stanza)
}
return;
} else {
const char* jid = jid_fulljid_or_barejid(xmpp_presence->jid);
char* jid = jid_fulljid_or_barejid(xmpp_presence->jid);
log_debug("Presence available handler fired for: %s", jid);
}
@@ -632,7 +632,7 @@ _available_handler(xmpp_stanza_t* const stanza)
XMPPCaps* caps = stanza_parse_caps(stanza);
if ((g_strcmp0(my_jid->fulljid, xmpp_presence->jid->fulljid) != 0) && caps) {
log_debug("Presence contains capabilities.");
const char* jid = jid_fulljid_or_barejid(xmpp_presence->jid);
char* jid = jid_fulljid_or_barejid(xmpp_presence->jid);
_handle_caps(jid, caps);
}
stanza_free_caps(caps);
@@ -644,7 +644,7 @@ _available_handler(xmpp_stanza_t* const stanza)
const char* account_name = session_get_account_name();
int max_sessions = accounts_get_max_sessions(account_name);
if (max_sessions > 0) {
auto_gchar gchar* cur_resource = accounts_get_resource(account_name);
const gchar* cur_resource = accounts_get_resource(account_name);
int res_count = connection_count_available_resources();
if (res_count > max_sessions && g_strcmp0(cur_resource, resource->name)) {
ProfWin* console = wins_get_console();

View File

@@ -275,8 +275,11 @@ roster_remove(const char* const name, const char* const barejid)
if (contact) {
GList* resources = p_contact_get_available_resources(contact);
while (resources) {
auto_gchar gchar* fulljid = g_strdup_printf("%s/%s", barejid, (char*)resources->data);
autocomplete_remove(roster->fulljid_ac, fulljid);
GString* fulljid = g_string_new(barejid);
g_string_append(fulljid, "/");
g_string_append(fulljid, resources->data);
autocomplete_remove(roster->fulljid_ac, fulljid->str);
g_string_free(fulljid, TRUE);
resources = g_list_next(resources);
}
g_list_free(resources);

View File

@@ -281,7 +281,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1");
assert_true(prof_output_exact("CProof. Type /help for help information."));
assert_true(prof_output_exact("Profanity. Type /help for help information."));
stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -322,7 +322,7 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1");
assert_true(prof_output_exact("CProof. Type /help for help information."));
assert_true(prof_output_exact("Profanity. Type /help for help information."));
stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -368,7 +368,7 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1");
assert_true(prof_output_exact("CProof. Type /help for help information."));
assert_true(prof_output_exact("Profanity. Type /help for help information."));
stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"

View File

@@ -219,7 +219,7 @@ returns_fulljid_when_exists(void** state)
{
Jid* jid = jid_create("localpart@domainpart/resourcepart");
const char* result = jid_fulljid_or_barejid(jid);
char* result = jid_fulljid_or_barejid(jid);
assert_string_equal("localpart@domainpart/resourcepart", result);
@@ -231,7 +231,7 @@ returns_barejid_when_fulljid_not_exists(void** state)
{
Jid* jid = jid_create("localpart@domainpart");
const char* result = jid_fulljid_or_barejid(jid);
char* result = jid_fulljid_or_barejid(jid);
assert_string_equal("localpart@domainpart", result);