Compare commits

..

1 Commits

Author SHA1 Message Date
3a23bb3273 Fix workflow
All checks were successful
CI / Check spelling (pull_request) Successful in 1m24s
CI / Check coding style (pull_request) Successful in 1m44s
CI / Linux (debian) (pull_request) Successful in 12m16s
CI / Linux (ubuntu) (pull_request) Successful in 13m11s
CI / Linux (fedora) (pull_request) Successful in 15m4s
CI / Linux (arch) (pull_request) Successful in 15m37s
Current setup of the infratstrcture with DIND
was causing issues with volume binding,
leading to a false-positive error on code formatting check.

This commit addresses the issue by removing dependency on a container,
but at the same time it limits choice of formatting version.

This fix does not address the underlying issue with the architecture.

As an additional change, package versions are updated.
2025-06-20 14:46:19 +02:00
48 changed files with 326 additions and 559 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: jubalh
patreon: # Replace with Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL

View File

@@ -20,6 +20,8 @@
<!--- Give us a screenshot (if it's helpful for this particular bug) -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->

View File

@@ -5,5 +5,5 @@
- [ ] I ran valgrind when using my new feature
### How to test the functionality
- step 1
- step 2
* step 1
* step 2

View File

@@ -20,7 +20,7 @@ jobs:
name: Linux
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Run tests
run: |
docker build -f Dockerfile.${{ matrix.flavor }} -t profanity .
@@ -78,11 +78,11 @@ jobs:
exit $failed
spell-check:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: Check spelling
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: install dependencies
run: |
sudo apt update

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

@@ -10,8 +10,8 @@ We recommend for people to always work on a dedicated git branch for each fix or
Don't work on master.
So that they can easily pull master and rebase their work if needed.
For fixing (reported) bugs we usually use `git switch -c fix/issuenumber-somedescription`.
When working on a new feature we usually use `git switch -c feature/optionalissuenumber-somedescription`.
For fixing (reported) bugs we usually use `git checkout -b fix/issuenumber-somedescription`.
When working on a new feature we usually use `git checkout -b feature/optionalissuenumber-somedescription`.
However this is not a rule just a recommendation to keep an overview of things.
If your change isn't a bugfix or new feature you can also just use any branch name.

View File

@@ -3,6 +3,8 @@
Profanity is a console based XMPP client inspired by [Irssi](http://www.irssi.org/).
If you like Profanity consider becoming a sponsor or [donate](https://profanity-im.github.io/donate.html) some money.
![alt tag](https://profanity-im.github.io/images/prof-2.png)
See the [User Guide](https://profanity-im.github.io/userguide.html) for information on installing and using Profanity.
@@ -10,10 +12,26 @@ See the [User Guide](https://profanity-im.github.io/userguide.html) for informat
## 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.
Feel free to follow us on [twitter](https://twitter.com/profanityim), join our [mailing list](https://lists.posteo.de/listinfo/profanity) and/or [MUC](xmpp:profanity@rooms.dismail.de?join).
## 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.
## Donations
We would highly appreciate it if you support us via [GitHub Sponsors](https://github.com/sponsors/jubalh/). Especially if you make feature requests or need help using Profanity.
Sponsoring enables us to spend time on Profanity.
An alternative way to support us would be to ask for our IBAN or use Bitcoin: `bc1qx265eat7hfasqkqmk9qf38delydnrnuvzhzy0x`.
Issues backed by a sponsor will be tagged with the [sponsored](https://github.com/profanity-im/profanity/issues?q=label%3Asponsored+) label.
Feature requests that we consider out of scope, either because of interest or because of time needed to implement them, will be marked with the [unfunded](https://github.com/profanity-im/profanity/issues?q=label%3Aunfunded) label.
Another way to sponsor us or get an issue solved is to comment on an issue that you are willing to sponsor it.
Tasks from our [sponsors](SPONSORS.md) will be tackled first.
Thank you! <3
## 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:
@@ -22,19 +40,26 @@ For more technical details check out our [CONTRIBUTING.md](CONTRIBUTING.md) file
## 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 have 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.
## Links
### Website
<!-- TODO:
URL: https://jabber.space/
-->
URL: https://profanity-im.github.io
Repo: https://git.jabber.space/devs/profanity
Repo: https://github.com/profanity-im/profanity-im.github.io
<!-- TODO: blog/docs link -->
### Blog
URL: https://profanity-im.github.io/blog
Repo: https://github.com/profanity-im/blog
### Mailinglist
Mailing List: https://lists.posteo.de/listinfo/profanity
### Chatroom
MUC: profanity@rooms.dismail.de
### Plugins
Plugins repository: https://github.com/profanity-im/profanity-plugins

View File

@@ -13,7 +13,7 @@
* Generate HTML docs (the docgen argument only works when package status is development)
`./profanity docgen`
* Determine if `libprofanity`'s version needs to be [increased](https://github.com/profanity-im/profanity/issues/973)
* Determine if libprofanitys version needs to be [increased](https://github.com/profanity-im/profanity/issues/973)
* Update plugin API docs (./apidocs/c and ./apidocs/python) need to run the `gen.sh` and commit the results to the website git repo
* Update CHANGELOG
* Update profrc.example

22
SPONSORS.md Normal file
View File

@@ -0,0 +1,22 @@
# Giving back
Thank you very much to all of our sponsors and donors (including those that want to stay private)!
## Sponsors
[Martin Dosch](https://github.com/mdosch) since January 2020
[Alexander Dahl](https://github.com/LeSpocky) since January 2021
[James Pond](https://github.com/jamesponddotco) since February 2021
## Donors
[Martin Dosch](https://github.com/mdosch)
[Julian Huhn](https://github.com/huhndev)
[Matteo Bini](https://github.com/matteobin)
## Services
[Stefan Kropp](https://github.com/StefanKropp) for sponsoring our [mailing list](https://lists.posteo.de/listinfo/profanity)
[dismail](https://dismail.de/) for hosting our [MUC](xmpp:profanity@rooms.dismail.de?join)

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

@@ -387,7 +387,7 @@ release_get_latest(void)
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, _data_callback);
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 2L);
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 2);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void*)&output);
curl_easy_perform(handle);

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

@@ -56,9 +56,10 @@
#include "config/preferences.h"
#include "config/color.h"
static GString* theme_loc = NULL;
static GKeyFile* theme = NULL;
static GHashTable* defaults = NULL;
static GString* theme_loc;
static GKeyFile* theme;
static GHashTable* bold_items;
static GHashTable* defaults;
static void _load_preferences(void);
static void _theme_list_dir(const gchar* const dir, GSList** result);
@@ -77,6 +78,10 @@ _theme_close(void)
g_string_free(theme_loc, TRUE);
theme_loc = NULL;
}
if (bold_items) {
g_hash_table_destroy(bold_items);
bold_items = NULL;
}
if (defaults) {
g_hash_table_destroy(defaults);
defaults = NULL;
@@ -86,12 +91,6 @@ _theme_close(void)
void
theme_init(const char* const theme_name)
{
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_close();
}
if (!_theme_load_file(theme_name)) {
log_error("Loading theme %s failed.", theme_name);

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

@@ -156,7 +156,14 @@ sv_ev_roster_received(void)
if (!p_gpg_valid_key(account->pgp_keyid, &err_str)) {
cons_show_error("Invalid PGP key ID specified: %s, %s", account->pgp_keyid, err_str);
}
// Redraw the screen after entry of the PGP secret key, but not init
ProfWin* win = wins_get_current();
auto_gchar gchar* theme = prefs_get_string(PREF_THEME);
win_redraw(win);
theme_init(theme);
ui_resize();
ui_show_roster();
}
account_free(account);
#endif

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

@@ -109,7 +109,7 @@ omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
{
return NULL;
};
void omemo_free(void* a) {};
void omemo_free(void* a){};
uint32_t
omemo_device_id()

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

View File

@@ -22,7 +22,7 @@ aesgcm_file_get(void* userdata)
return NULL;
};
void aesgcm_download_cancel_processes(ProfWin* window) {};
void aesgcm_download_add_download(AESGCMDownload* download) {};
void aesgcm_download_cancel_processes(ProfWin* window){};
void aesgcm_download_add_download(AESGCMDownload* download){};
#endif

View File

@@ -27,7 +27,7 @@ http_file_get(void* userdata)
return NULL;
}
void http_download_cancel_processes() {};
void http_download_add_download() {};
void http_download_cancel_processes(){};
void http_download_add_download(){};
#endif

View File

@@ -38,7 +38,7 @@ file_size(int filedes)
return 0;
}
void http_upload_cancel_processes() {};
void http_upload_add_upload() {};
void http_upload_cancel_processes(){};
void http_upload_add_upload(){};
#endif

View File

@@ -484,10 +484,10 @@ mucwin_unset_message_char(ProfMucWin* mucwin)
{
}
void win_update_entry_message(ProfWin* window, const char* const id, const char* const message) {};
void win_mark_received(ProfWin* window, const char* const id) {};
void win_print_http_transfer(ProfWin* window, const char* const message, char* id) {};
void win_print_loading_history(ProfWin* window) {};
void win_update_entry_message(ProfWin* window, const char* const id, const char* const message){};
void win_mark_received(ProfWin* window, const char* const id){};
void win_print_http_transfer(ProfWin* window, const char* const message, char* id){};
void win_print_loading_history(ProfWin* window){};
void
ui_show_roster(void)

View File

@@ -2,7 +2,7 @@
#include <glib.h>
#include <stdlib.h>
void avatar_pep_subscribe(void) {};
void avatar_pep_subscribe(void){};
gboolean
avatar_get_by_nick(const char* nick, gboolean open)

View File

@@ -312,8 +312,8 @@ presence_sub_request_exists(const char* const bare_jid)
}
// iq functions
void iq_disable_carbons() {};
void iq_enable_carbons() {};
void iq_disable_carbons(){};
void iq_enable_carbons(){};
void
iq_send_software_version(const char* const fulljid)
{