merge: sync upstream profanity-im/profanity
All checks were successful
CI Code / Check spelling (push) Successful in 18s
CI Code / Check coding style (push) Successful in 34s
CI Code / Code Coverage (push) Successful in 2m36s
CI Code / Linux (debian) (push) Successful in 4m41s
CI Code / Linux (ubuntu) (push) Successful in 4m52s
CI Code / Linux (arch) (push) Successful in 5m40s

Sync with upstream profanity-im/profanity.

Major upstream changes incorporated:

Memory management
  - Replace malloc+memset with g_new0 throughout codebase
  - Adopt auto_gchar / auto_gcharv / auto_gerror / auto_jid cleanup macros
  - Replace free() with g_free() for GAlloc'd memory

Editor rewrite
  - Remove pthread-based async editor; use GChildWatch callback API
  - New launch_editor(initial_content, callback, user_data) interface
  - Proper signal handling (SIGINT, SIGTSTP, SIGPIPE reset in child)
  - ui_suspend()/ui_resume() integration for TTY management

OMEMO improvements
  - Dual backend support: libsignal-protocol-c and libomemo-c
  - Proper pre-key removal after use (XEP-0384 compliance)
  - Automatic pre-key regeneration when store drops below threshold
  - New functions: omemo_is_device_active(), omemo_is_jid_trusted()
  - omemo_get_jid_untrusted_fingerprints() for better error messages
  - Fingerprint notifications on new device identity discovery
  - Deterministic pre-key ID generation tracking max_pre_key_id
  - omemo_trust_changed() UI updates on trust state changes

JID validation
  - RFC 6122-compliant validation in jid_is_valid()
  - Character-level checks (RFC 6122 forbidden chars: & ' / : < > @)
  - Length limits: 1023 per component, 3071 total
  - New jid_is_valid_user_jid() for user vs. service JID distinction

Database
  - Schema migration v3: UNIQUE constraint on archive_id for deduplication
  - Triggers for corrected message tracking (replaces_db_id / replaced_by_db_id)
  - db_history_result_t return type, _truncate_datetime_suffix()

UI / console
  - win_warn_needed() / win_warn_sent() warning deduplication hash table
  - PAD_MIN_HEIGHT dynamic pad sizing with PAD_THRESHOLD auto-cleanup
  - Spellcheck integration in input field with Unicode word detection
  - cons_spellcheck_setting() for /settings ui output
  - /[command]? shortcut for command help

Account config
  - Account name sanitization for GKeyFile special chars ([ ] = # \n \r)
  - Replace popen() with g_spawn_sync() for eval_password
  - TLS policy: add "direct" option alongside legacy

Connection
  - Port validation with g_assert (0–65535)
  - SHA-256 certificate fingerprint support (XMPP_CERT_PUBKEY_FINGERPRINT_SHA256)
  - "direct" TLS policy alias for legacy SSL

Common utilities
  - str_xml_sanitize() for XML 1.0 illegal character removal
  - string_matches_one_of() with formatted error messages
  - valid_tls_policy_option() helper
  - prof_date_time_format_iso8601() utility
  - Improved strip_arg_quotes() with backslash unescaping
  - prof_occurrences() uses g_slist_prepend + reverse for performance

PGP / OX
  - Proper GPGME resource cleanup with goto-cleanup pattern
  - g_string_free(xmppuri) leak fix in _ox_key_lookup

CSV export
  - Use GString + g_file_set_contents instead of raw write() syscalls

Tests
  - Restructured into subdirectories: command/, config/, xmpp/, ui/, omemo/, otr/, pgp/
  - New test_cmd_ac.c for autocompleter unit tests
  - Updated stubs for new UI suspend/resume functions

License headers
  - Migrate to SPDX-3.0 identifiers (GPL-3.0-or-later WITH OpenSSL-exception)

────────────────────────────────────────────────────
cproof-specific preservations:

  - XEP-0308 LMC: replace_id ?: id logic in message/stanza/omemo
  - Force encryption: cmd_force_encryption, test_forced_encryption
  - CWE-134: format string protection (cons_show("%s", ...))
  - y_start_pos-based paging in window.c
  - db_history_result_t return type, _truncate_datetime_suffix()

Merge-time fixes:

  - common.c: format-security (-Werror) — cons_show(errmsg) → cons_show("%s", errmsg)
  - database.c: null-deref guard — !msg->timestamp → msg && !msg->timestamp
  - console.c: implicit size_t → int cast — (int)(maxlen + 1)
  - tlscerts.c: %d for size_t — %zu

Build system:
  - Kept autotools (Makefile.am, configure.ac); upstream uses Meson
  - Restored deleted files: bootstrap.sh, autogen.sh, ax_valgrind_check.m4, configure-debug
  - Updated Makefile.am test paths for subdirectory structure
  - Added test_cmd_ac, test_forced_encryption to test sources

Functional tests:
  - Use cproof version; upstream requires stbbr_for_xmlns from updated stabber
  - Not yet available in devs/stabber fork

Closes #64

Merge author: jabber.developer2
Commits authors:
 Michael Vetter <jubalh@iodoru.org>
& Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
2026-05-26 17:48:14 +00:00
parent 3b673150b4
commit 72f4f186da
303 changed files with 10658 additions and 9857 deletions

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -39,6 +13,7 @@
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/wait.h>
#include <glib.h>
@@ -59,7 +34,7 @@ account_new(gchar* name, gchar* jid, gchar* password, gchar* eval_password, gboo
gchar* startscript, gchar* theme, gchar* tls_policy, gchar* auth_policy,
gchar* client, int max_sessions)
{
ProfAccount* new_account = calloc(1, sizeof(ProfAccount));
ProfAccount* new_account = g_new0(ProfAccount, 1);
new_account->name = name;
@@ -144,13 +119,13 @@ account_new(gchar* name, gchar* jid, gchar* password, gchar* eval_password, gboo
return new_account;
}
char*
gchar*
account_create_connect_jid(ProfAccount* account)
{
if (account->resource) {
return create_fulljid(account->jid, account->resource);
} else {
return strdup(account->jid);
return g_strdup(account->jid);
}
}
@@ -160,53 +135,41 @@ account_eval_password(ProfAccount* account)
assert(account != NULL);
assert(account->eval_password != NULL);
errno = 0;
gchar* stdout_buf = NULL;
GError* error = NULL;
gint exit_status = 0;
gchar** argv = NULL;
FILE* stream = popen(account->eval_password, "r");
if (stream == NULL) {
const char* errmsg = strerror(errno);
if (errmsg) {
log_error("Could not execute `eval_password` command (%s).",
errmsg);
} else {
log_error("Failed to allocate memory for `eval_password` command.");
if (!g_shell_parse_argv(account->eval_password, NULL, &argv, &error)) {
log_error("Failed to parse `eval_password` command: %s", error->message);
g_error_free(error);
return FALSE;
}
if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, &stdout_buf, NULL, &exit_status, &error)) {
log_error("Failed to execute `eval_password` command: %s", error->message);
g_strfreev(argv);
g_error_free(error);
return FALSE;
}
g_strfreev(argv);
if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) {
account->password = stdout_buf;
g_strstrip(account->password);
if (account->password[0] == '\0') {
log_error("Empty password returned by `eval_password` command.");
g_free(account->password);
account->password = NULL;
return FALSE;
}
return TRUE;
} else {
log_error("`eval_password` command failed with status %d", exit_status);
g_free(stdout_buf);
return FALSE;
}
account->password = g_malloc(READ_BUF_SIZE);
if (!account->password) {
log_error("Failed to allocate enough memory to read `eval_password` "
"output.");
return FALSE;
}
account->password = fgets(account->password, READ_BUF_SIZE, stream);
if (!account->password) {
log_error("Failed to read password from stream.");
return FALSE;
}
int exit_status = pclose(stream);
if (exit_status > 0) {
log_error("Command for `eval_password` returned error status (%d).",
exit_status);
return FALSE;
} else if (exit_status < 0) {
log_error("Failed to close stream for `eval_password` command output "
"(%s).",
strerror(errno));
return FALSE;
};
// Remove leading and trailing whitespace from output.
g_strstrip(account->password);
if (!account->password) {
log_error("Empty password returned by `eval_password` command.");
return FALSE;
}
return TRUE;
}
void

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_ACCOUNT_H
@@ -84,7 +58,7 @@ ProfAccount* account_new(gchar* name, gchar* jid, gchar* password, gchar* eval_p
GList* ox_enabled, GList* pgp_enabled, gchar* pgp_keyid,
gchar* startscript, gchar* theme, gchar* tls_policy, gchar* auth_policy,
gchar* client, int max_sessions);
char* account_create_connect_jid(ProfAccount* account);
gchar* account_create_connect_jid(ProfAccount* account);
gboolean account_eval_password(ProfAccount* account);
void account_free(ProfAccount* account);
void account_set_server(ProfAccount* account, const char* server);

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -59,7 +33,74 @@ static GKeyFile* accounts;
static Autocomplete all_ac;
static Autocomplete enabled_ac;
static void _save_accounts(void);
static gchar*
_sanitize_account_name(const char* const name)
{
if (!name) {
return NULL;
}
gchar* sanitized = g_strdup(name);
gchar* p = sanitized;
while (*p) {
// GKeyFile special characters: [ ] = # \n \r
if (*p == '[' || *p == ']' || *p == '=' || *p == '#' || *p == '\n' || *p == '\r') {
*p = '_';
}
p++;
}
return sanitized;
}
static gboolean
_accounts_has_group(const char* account_name)
{
if (!account_name || !accounts)
return FALSE;
auto_gchar gchar* sanitized = _sanitize_account_name(account_name);
return g_key_file_has_group(accounts, sanitized);
}
static void
_accounts_save(const char* account_name)
{
prof_keyfile_t current;
if (!load_data_keyfile(&current, FILE_ACCOUNTS)) {
log_error("Could not load accounts");
return;
}
auto_gchar gchar* sanitized = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized)) {
// Remove keys from file that are no longer in memory
gsize nkeys_disk;
auto_gcharv gchar** keys_disk = g_key_file_get_keys(current.keyfile, sanitized, &nkeys_disk, NULL);
if (keys_disk) {
for (gsize j = 0; j < nkeys_disk; ++j) {
if (!g_key_file_has_key(accounts_prof_keyfile.keyfile, sanitized, keys_disk[j], NULL)) {
g_key_file_remove_key(current.keyfile, sanitized, keys_disk[j], NULL);
}
}
}
gsize nkeys;
auto_gcharv gchar** keys = g_key_file_get_keys(accounts_prof_keyfile.keyfile, sanitized, &nkeys, NULL);
if (keys) {
for (gsize j = 0; j < nkeys; ++j) {
auto_gchar gchar* new_value = g_key_file_get_value(accounts_prof_keyfile.keyfile, sanitized, keys[j], NULL);
g_key_file_set_value(current.keyfile, sanitized, keys[j], new_value);
}
}
} else {
g_key_file_remove_group(current.keyfile, sanitized, NULL);
}
save_keyfile(&current);
free_keyfile(&current);
}
static void
_accounts_close(void)
@@ -79,7 +120,9 @@ accounts_load(void)
all_ac = autocomplete_new();
enabled_ac = autocomplete_new();
load_data_keyfile(&accounts_prof_keyfile, FILE_ACCOUNTS);
if (!load_data_keyfile(&accounts_prof_keyfile, FILE_ACCOUNTS)) {
log_error("Could not load accounts");
}
accounts = accounts_prof_keyfile.keyfile;
// create the logins searchable list for autocompletion
@@ -132,54 +175,58 @@ accounts_add(const char* account_name, const char* altdomain, const int port, co
}
}
if (g_key_file_has_group(accounts, account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
log_error("Can't add account \"%s\", it already exists.", account_name);
return;
}
g_key_file_set_boolean(accounts, account_name, "enabled", TRUE);
g_key_file_set_string(accounts, account_name, "jid", barejid);
g_key_file_set_string(accounts, account_name, "resource", resource);
g_key_file_set_boolean(accounts, sanitized_account_name, "enabled", TRUE);
g_key_file_set_string(accounts, sanitized_account_name, "jid", barejid);
g_key_file_set_string(accounts, sanitized_account_name, "resource", resource);
if (altdomain) {
g_key_file_set_string(accounts, account_name, "server", altdomain);
g_key_file_set_string(accounts, sanitized_account_name, "server", altdomain);
}
if (port != 0) {
g_key_file_set_integer(accounts, account_name, "port", port);
g_key_file_set_integer(accounts, sanitized_account_name, "port", port);
}
if (tls_policy) {
g_key_file_set_string(accounts, account_name, "tls.policy", tls_policy);
g_key_file_set_string(accounts, sanitized_account_name, "tls.policy", tls_policy);
}
if (auth_policy) {
g_key_file_set_string(accounts, account_name, "auth.policy", auth_policy);
g_key_file_set_string(accounts, sanitized_account_name, "auth.policy", auth_policy);
}
auto_jid Jid* jidp = jid_create(barejid);
if (jidp->localpart == NULL) {
g_key_file_set_string(accounts, account_name, "muc.nick", jidp->domainpart);
g_key_file_set_string(accounts, sanitized_account_name, "muc.nick", jidp->domainpart);
} else {
g_key_file_set_string(accounts, account_name, "muc.nick", jidp->localpart);
g_key_file_set_string(accounts, sanitized_account_name, "muc.nick", jidp->localpart);
}
g_key_file_set_string(accounts, account_name, "presence.last", "online");
g_key_file_set_string(accounts, account_name, "presence.login", "online");
g_key_file_set_integer(accounts, account_name, "priority.online", 0);
g_key_file_set_integer(accounts, account_name, "priority.chat", 0);
g_key_file_set_integer(accounts, account_name, "priority.away", 0);
g_key_file_set_integer(accounts, account_name, "priority.xa", 0);
g_key_file_set_integer(accounts, account_name, "priority.dnd", 0);
g_key_file_set_string(accounts, sanitized_account_name, "presence.last", "online");
g_key_file_set_string(accounts, sanitized_account_name, "presence.login", "online");
g_key_file_set_integer(accounts, sanitized_account_name, "priority.online", 0);
g_key_file_set_integer(accounts, sanitized_account_name, "priority.chat", 0);
g_key_file_set_integer(accounts, sanitized_account_name, "priority.away", 0);
g_key_file_set_integer(accounts, sanitized_account_name, "priority.xa", 0);
g_key_file_set_integer(accounts, sanitized_account_name, "priority.dnd", 0);
_save_accounts();
autocomplete_add(all_ac, account_name);
autocomplete_add(enabled_ac, account_name);
_accounts_save(account_name);
autocomplete_add(all_ac, sanitized_account_name);
autocomplete_add(enabled_ac, sanitized_account_name);
}
int
gboolean
accounts_remove(const char* account_name)
{
int r = g_key_file_remove_group(accounts, account_name, NULL);
_save_accounts();
autocomplete_remove(all_ac, account_name);
autocomplete_remove(enabled_ac, account_name);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
gboolean r = g_key_file_remove_group(accounts, sanitized_account_name, NULL);
_accounts_save(account_name);
autocomplete_remove(all_ac, sanitized_account_name);
autocomplete_remove(enabled_ac, sanitized_account_name);
return r;
}
@@ -189,167 +236,123 @@ accounts_get_list(void)
return g_key_file_get_groups(accounts, NULL);
}
ProfAccount*
accounts_get_account(const char* const name)
static GList*
_g_strv_to_glist(gchar** in, gsize length)
{
if (!g_key_file_has_group(accounts, name)) {
if (in == NULL)
return NULL;
GList* out = NULL;
for (gsize i = 0; i < length; i++) {
out = g_list_append(out, in[i]);
}
g_free(in);
return out;
}
static GList*
_accounts_get_glist(const gchar* sanitized_account_name,
const gchar* key)
{
gsize length = 0;
gchar** list = g_key_file_get_string_list(accounts, sanitized_account_name, key, &length, NULL);
return _g_strv_to_glist(list, length);
}
ProfAccount*
accounts_get_account(const char* const account_name)
{
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (!_accounts_has_group(sanitized_account_name)) {
return NULL;
} else {
gchar* jid = g_key_file_get_string(accounts, name, "jid", NULL);
gchar* jid = g_key_file_get_string(accounts, sanitized_account_name, "jid", NULL);
// fix accounts that have no jid property by setting to name
// fix accounts that have no jid property by setting to account_name
if (jid == NULL) {
g_key_file_set_string(accounts, name, "jid", name);
_save_accounts();
g_key_file_set_string(accounts, sanitized_account_name, "jid", account_name);
_accounts_save(account_name);
}
gchar* password = g_key_file_get_string(accounts, name, "password", NULL);
gchar* eval_password = g_key_file_get_string(accounts, name, "eval_password", NULL);
gboolean enabled = g_key_file_get_boolean(accounts, name, "enabled", NULL);
gchar* password = g_key_file_get_string(accounts, sanitized_account_name, "password", NULL);
gchar* eval_password = g_key_file_get_string(accounts, sanitized_account_name, "eval_password", NULL);
gboolean enabled = g_key_file_get_boolean(accounts, sanitized_account_name, "enabled", NULL);
gchar* server = g_key_file_get_string(accounts, name, "server", NULL);
gchar* resource = g_key_file_get_string(accounts, name, "resource", NULL);
int port = g_key_file_get_integer(accounts, name, "port", NULL);
gchar* server = g_key_file_get_string(accounts, sanitized_account_name, "server", NULL);
gchar* resource = g_key_file_get_string(accounts, sanitized_account_name, "resource", NULL);
int port = g_key_file_get_integer(accounts, sanitized_account_name, "port", NULL);
gchar* last_presence = g_key_file_get_string(accounts, name, "presence.last", NULL);
gchar* login_presence = g_key_file_get_string(accounts, name, "presence.login", NULL);
gchar* last_presence = g_key_file_get_string(accounts, sanitized_account_name, "presence.last", NULL);
gchar* login_presence = g_key_file_get_string(accounts, sanitized_account_name, "presence.login", NULL);
int priority_online = g_key_file_get_integer(accounts, name, "priority.online", NULL);
int priority_chat = g_key_file_get_integer(accounts, name, "priority.chat", NULL);
int priority_away = g_key_file_get_integer(accounts, name, "priority.away", NULL);
int priority_xa = g_key_file_get_integer(accounts, name, "priority.xa", NULL);
int priority_dnd = g_key_file_get_integer(accounts, name, "priority.dnd", NULL);
int priority_online = g_key_file_get_integer(accounts, sanitized_account_name, "priority.online", NULL);
int priority_chat = g_key_file_get_integer(accounts, sanitized_account_name, "priority.chat", NULL);
int priority_away = g_key_file_get_integer(accounts, sanitized_account_name, "priority.away", NULL);
int priority_xa = g_key_file_get_integer(accounts, sanitized_account_name, "priority.xa", NULL);
int priority_dnd = g_key_file_get_integer(accounts, sanitized_account_name, "priority.dnd", NULL);
gchar* muc_service = NULL;
if (g_key_file_has_key(accounts, name, "muc.service", NULL)) {
muc_service = g_key_file_get_string(accounts, name, "muc.service", NULL);
if (g_key_file_has_key(accounts, sanitized_account_name, "muc.service", NULL)) {
muc_service = g_key_file_get_string(accounts, sanitized_account_name, "muc.service", NULL);
} else {
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status == JABBER_CONNECTED) {
const char* conf_jid = connection_jid_for_feature(XMPP_FEATURE_MUC);
if (conf_jid) {
muc_service = strdup(conf_jid);
muc_service = g_strdup(conf_jid);
}
}
}
gchar* muc_nick = g_key_file_get_string(accounts, name, "muc.nick", NULL);
gchar* muc_nick = g_key_file_get_string(accounts, sanitized_account_name, "muc.nick", NULL);
gchar* otr_policy = NULL;
if (g_key_file_has_key(accounts, name, "otr.policy", NULL)) {
otr_policy = g_key_file_get_string(accounts, name, "otr.policy", NULL);
}
gchar* otr_policy = g_key_file_get_string(accounts, sanitized_account_name, "otr.policy", NULL);
GList* otr_manual = _accounts_get_glist(sanitized_account_name, "otr.manual");
GList* otr_opportunistic = _accounts_get_glist(sanitized_account_name, "otr.opportunistic");
GList* otr_always = _accounts_get_glist(sanitized_account_name, "otr.always");
gsize length;
GList* otr_manual = NULL;
auto_gcharv gchar** manual = g_key_file_get_string_list(accounts, name, "otr.manual", &length, NULL);
if (manual) {
for (int i = 0; i < length; i++) {
otr_manual = g_list_append(otr_manual, strdup(manual[i]));
}
}
gchar* omemo_policy = g_key_file_get_string(accounts, sanitized_account_name, "omemo.policy", NULL);
GList* omemo_enabled = _accounts_get_glist(sanitized_account_name, "omemo.enabled");
GList* omemo_disabled = _accounts_get_glist(sanitized_account_name, "omemo.disabled");
GList* otr_opportunistic = NULL;
auto_gcharv gchar** opportunistic = g_key_file_get_string_list(accounts, name, "otr.opportunistic", &length, NULL);
if (opportunistic) {
for (int i = 0; i < length; i++) {
otr_opportunistic = g_list_append(otr_opportunistic, strdup(opportunistic[i]));
}
}
GList* ox_enabled = _accounts_get_glist(sanitized_account_name, "ox.enabled");
GList* otr_always = NULL;
auto_gcharv gchar** always = g_key_file_get_string_list(accounts, name, "otr.always", &length, NULL);
if (always) {
for (int i = 0; i < length; i++) {
otr_always = g_list_append(otr_always, strdup(always[i]));
}
}
GList* pgp_enabled = _accounts_get_glist(sanitized_account_name, "pgp.enabled");
gchar* omemo_policy = NULL;
if (g_key_file_has_key(accounts, name, "omemo.policy", NULL)) {
omemo_policy = g_key_file_get_string(accounts, name, "omemo.policy", NULL);
}
gchar* pgp_keyid = g_key_file_get_string(accounts, sanitized_account_name, "pgp.keyid", NULL);
GList* omemo_enabled = NULL;
auto_gcharv gchar** omemo_enabled_list = g_key_file_get_string_list(accounts, name, "omemo.enabled", &length, NULL);
if (omemo_enabled_list) {
for (int i = 0; i < length; i++) {
omemo_enabled = g_list_append(omemo_enabled, strdup(omemo_enabled_list[i]));
}
}
gchar* startscript = g_key_file_get_string(accounts, sanitized_account_name, "script.start", NULL);
GList* omemo_disabled = NULL;
auto_gcharv gchar** omemo_disabled_list = g_key_file_get_string_list(accounts, name, "omemo.disabled", &length, NULL);
if (omemo_disabled_list) {
for (int i = 0; i < length; i++) {
omemo_disabled = g_list_append(omemo_disabled, strdup(omemo_disabled_list[i]));
}
}
gchar* client = g_key_file_get_string(accounts, sanitized_account_name, "client.account_name", NULL);
GList* ox_enabled = NULL;
auto_gcharv gchar** ox_enabled_list = g_key_file_get_string_list(accounts, name, "ox.enabled", &length, NULL);
if (ox_enabled_list) {
for (int i = 0; i < length; i++) {
ox_enabled = g_list_append(ox_enabled, strdup(ox_enabled_list[i]));
}
}
gchar* theme = g_key_file_get_string(accounts, sanitized_account_name, "theme", NULL);
GList* pgp_enabled = NULL;
auto_gcharv gchar** pgp_enabled_list = g_key_file_get_string_list(accounts, name, "pgp.enabled", &length, NULL);
if (pgp_enabled_list) {
for (int i = 0; i < length; i++) {
pgp_enabled = g_list_append(pgp_enabled, strdup(pgp_enabled_list[i]));
}
}
gchar* pgp_keyid = NULL;
if (g_key_file_has_key(accounts, name, "pgp.keyid", NULL)) {
pgp_keyid = g_key_file_get_string(accounts, name, "pgp.keyid", NULL);
}
gchar* startscript = NULL;
if (g_key_file_has_key(accounts, name, "script.start", NULL)) {
startscript = g_key_file_get_string(accounts, name, "script.start", NULL);
}
gchar* client = NULL;
if (g_key_file_has_key(accounts, name, "client.name", NULL)) {
client = g_key_file_get_string(accounts, name, "client.name", NULL);
}
gchar* theme = NULL;
if (g_key_file_has_key(accounts, name, "theme", NULL)) {
theme = g_key_file_get_string(accounts, name, "theme", NULL);
}
gchar* tls_policy = g_key_file_get_string(accounts, name, "tls.policy", NULL);
if (tls_policy && ((g_strcmp0(tls_policy, "force") != 0) && (g_strcmp0(tls_policy, "allow") != 0) && (g_strcmp0(tls_policy, "trust") != 0) && (g_strcmp0(tls_policy, "disable") != 0) && (g_strcmp0(tls_policy, "legacy") != 0))) {
gchar* tls_policy = g_key_file_get_string(accounts, sanitized_account_name, "tls.policy", NULL);
if (tls_policy && !valid_tls_policy_option(tls_policy)) {
g_free(tls_policy);
tls_policy = NULL;
}
gchar* auth_policy = g_key_file_get_string(accounts, name, "auth.policy", NULL);
gchar* auth_policy = g_key_file_get_string(accounts, sanitized_account_name, "auth.policy", NULL);
int max_sessions = g_key_file_get_integer(accounts, name, "max.sessions", 0);
int max_sessions = g_key_file_get_integer(accounts, sanitized_account_name, "max.sessions", 0);
ProfAccount* new_account = account_new(g_strdup(name), jid, password, eval_password, enabled,
server, port, resource, last_presence, login_presence,
priority_online, priority_chat, priority_away, priority_xa,
priority_dnd, muc_service, muc_nick, otr_policy, otr_manual,
otr_opportunistic, otr_always, omemo_policy, omemo_enabled,
omemo_disabled, ox_enabled, pgp_enabled, pgp_keyid,
startscript, theme, tls_policy, auth_policy, client, max_sessions);
return new_account;
return account_new(g_strdup(account_name), jid, password, eval_password, enabled,
server, port, resource, last_presence, login_presence,
priority_online, priority_chat, priority_away, priority_xa,
priority_dnd, muc_service, muc_nick, otr_policy, otr_manual,
otr_opportunistic, otr_always, omemo_policy, omemo_enabled,
omemo_disabled, ox_enabled, pgp_enabled, pgp_keyid,
startscript, theme, tls_policy, auth_policy, client, max_sessions);
}
}
gboolean
accounts_enable(const char* const name)
accounts_enable(const char* const account_name)
{
if (g_key_file_has_group(accounts, name)) {
g_key_file_set_boolean(accounts, name, "enabled", TRUE);
_save_accounts();
autocomplete_add(enabled_ac, name);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_boolean(accounts, sanitized_account_name, "enabled", TRUE);
_accounts_save(account_name);
autocomplete_add(enabled_ac, sanitized_account_name);
return TRUE;
} else {
return FALSE;
@@ -357,12 +360,13 @@ accounts_enable(const char* const name)
}
gboolean
accounts_disable(const char* const name)
accounts_disable(const char* const account_name)
{
if (g_key_file_has_group(accounts, name)) {
g_key_file_set_boolean(accounts, name, "enabled", FALSE);
_save_accounts();
autocomplete_remove(enabled_ac, name);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_boolean(accounts, sanitized_account_name, "enabled", FALSE);
_accounts_save(account_name);
autocomplete_remove(enabled_ac, sanitized_account_name);
return TRUE;
} else {
return FALSE;
@@ -372,63 +376,33 @@ accounts_disable(const char* const name)
gboolean
accounts_rename(const char* const account_name, const char* const new_name)
{
if (g_key_file_has_group(accounts, new_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
auto_gchar gchar* sanitized_new_account_name = _sanitize_account_name(new_name);
if (_accounts_has_group(sanitized_new_account_name)) {
return FALSE;
}
if (!g_key_file_has_group(accounts, account_name)) {
if (!_accounts_has_group(sanitized_account_name)) {
return FALSE;
}
// treat all properties as strings for copy
gchar* string_keys[] = {
"enabled",
"jid",
"server",
"port",
"resource",
"password",
"eval_password",
"presence.last",
"presence.laststatus",
"presence.login",
"priority.online",
"priority.chat",
"priority.away",
"priority.xa",
"priority.dnd",
"muc.service",
"muc.nick",
"otr.policy",
"otr.manual",
"otr.opportunistic",
"otr.always",
"omemo.policy",
"omemo.enabled",
"omemo.disabled",
"ox.enabled",
"pgp.enabled",
"pgp.keyid",
"last.activity",
"script.start",
"tls.policy"
};
for (int i = 0; i < ARRAY_SIZE(string_keys); i++) {
auto_gchar gchar* value = g_key_file_get_string(accounts, account_name, string_keys[i], NULL);
if (value) {
g_key_file_set_string(accounts, new_name, string_keys[i], value);
}
gsize nkeys;
auto_gcharv gchar** keys = g_key_file_get_keys(accounts, sanitized_account_name, &nkeys, NULL);
for (gsize i = 0; i < nkeys; i++) {
auto_gchar gchar* new_value = g_key_file_get_value(accounts, sanitized_account_name, keys[i], NULL);
g_key_file_set_value(accounts, sanitized_new_account_name, keys[i], new_value);
}
g_key_file_remove_group(accounts, account_name, NULL);
_save_accounts();
g_key_file_remove_group(accounts, sanitized_account_name, NULL);
_accounts_save(account_name);
_accounts_save(new_name);
autocomplete_remove(all_ac, account_name);
autocomplete_add(all_ac, new_name);
if (g_key_file_get_boolean(accounts, new_name, "enabled", NULL)) {
autocomplete_remove(enabled_ac, account_name);
autocomplete_add(enabled_ac, new_name);
autocomplete_remove(all_ac, sanitized_account_name);
autocomplete_remove(enabled_ac, sanitized_account_name);
autocomplete_add(all_ac, sanitized_new_account_name);
if (g_key_file_get_boolean(accounts, sanitized_new_account_name, "enabled", NULL)) {
autocomplete_add(enabled_ac, sanitized_new_account_name);
}
return TRUE;
@@ -437,7 +411,7 @@ accounts_rename(const char* const account_name, const char* const new_name)
gboolean
accounts_account_exists(const char* const account_name)
{
return g_key_file_has_group(accounts, account_name);
return _accounts_has_group(account_name);
}
void
@@ -445,19 +419,20 @@ accounts_set_jid(const char* const account_name, const char* const value)
{
auto_jid Jid* jid = jid_create(value);
if (jid) {
if (accounts_account_exists(account_name)) {
g_key_file_set_string(accounts, account_name, "jid", jid->barejid);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_string(accounts, sanitized_account_name, "jid", jid->barejid);
if (jid->resourcepart) {
g_key_file_set_string(accounts, account_name, "resource", jid->resourcepart);
g_key_file_set_string(accounts, sanitized_account_name, "resource", jid->resourcepart);
}
if (jid->localpart == NULL) {
g_key_file_set_string(accounts, account_name, "muc.nick", jid->domainpart);
g_key_file_set_string(accounts, sanitized_account_name, "muc.nick", jid->domainpart);
} else {
g_key_file_set_string(accounts, account_name, "muc.nick", jid->localpart);
g_key_file_set_string(accounts, sanitized_account_name, "muc.nick", jid->localpart);
}
_save_accounts();
_accounts_save(account_name);
}
}
}
@@ -465,9 +440,10 @@ accounts_set_jid(const char* const account_name, const char* const value)
void
accounts_set_server(const char* const account_name, const char* const value)
{
if (accounts_account_exists(account_name)) {
g_key_file_set_string(accounts, account_name, "server", value);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_string(accounts, sanitized_account_name, "server", value);
_accounts_save(account_name);
}
}
@@ -475,35 +451,41 @@ void
accounts_set_port(const char* const account_name, const int value)
{
if (value != 0) {
g_key_file_set_integer(accounts, account_name, "port", value);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_integer(accounts, sanitized_account_name, "port", value);
_accounts_save(account_name);
}
}
}
static void
_accounts_set_string_option(const char* account_name, const char* const option, const char* const value)
{
if (accounts_account_exists(account_name)) {
g_key_file_set_string(accounts, account_name, option, value ?: "");
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_string(accounts, sanitized_account_name, option, value ?: "");
_accounts_save(account_name);
}
}
static void
_accounts_set_int_option(const char* account_name, const char* const option, int value)
{
if (accounts_account_exists(account_name)) {
g_key_file_set_integer(accounts, account_name, option, value);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_integer(accounts, sanitized_account_name, option, value);
_accounts_save(account_name);
}
}
static void
_accounts_clear_string_option(const char* account_name, const char* const option)
{
if (accounts_account_exists(account_name)) {
g_key_file_remove_key(accounts, account_name, option, NULL);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_remove_key(accounts, sanitized_account_name, option, NULL);
_accounts_save(account_name);
}
}
@@ -630,83 +612,88 @@ accounts_clear_max_sessions(const char* const account_name)
void
accounts_add_otr_policy(const char* const account_name, const char* const contact_jid, const char* const policy)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
GString* key = g_string_new("otr.");
g_string_append(key, policy);
conf_string_list_add(accounts, account_name, key->str, contact_jid);
conf_string_list_add(accounts, sanitized_account_name, key->str, contact_jid);
g_string_free(key, TRUE);
// check for and remove from other lists
if (strcmp(policy, "manual") == 0) {
conf_string_list_remove(accounts, account_name, "otr.opportunistic", contact_jid);
conf_string_list_remove(accounts, account_name, "otr.always", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.opportunistic", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.always", contact_jid);
}
if (strcmp(policy, "opportunistic") == 0) {
conf_string_list_remove(accounts, account_name, "otr.manual", contact_jid);
conf_string_list_remove(accounts, account_name, "otr.always", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.manual", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.always", contact_jid);
}
if (strcmp(policy, "always") == 0) {
conf_string_list_remove(accounts, account_name, "otr.opportunistic", contact_jid);
conf_string_list_remove(accounts, account_name, "otr.manual", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.opportunistic", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "otr.manual", contact_jid);
}
_save_accounts();
_accounts_save(account_name);
}
}
void
accounts_add_omemo_state(const char* const account_name, const char* const contact_jid, gboolean enabled)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
if (enabled) {
conf_string_list_add(accounts, account_name, "omemo.enabled", contact_jid);
conf_string_list_remove(accounts, account_name, "omemo.disabled", contact_jid);
conf_string_list_add(accounts, sanitized_account_name, "omemo.enabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "omemo.disabled", contact_jid);
} else {
conf_string_list_add(accounts, account_name, "omemo.disabled", contact_jid);
conf_string_list_remove(accounts, account_name, "omemo.enabled", contact_jid);
conf_string_list_add(accounts, sanitized_account_name, "omemo.disabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "omemo.enabled", contact_jid);
}
_save_accounts();
_accounts_save(account_name);
}
}
void
accounts_add_ox_state(const char* const account_name, const char* const contact_jid, gboolean enabled)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
if (enabled) {
conf_string_list_add(accounts, account_name, "ox.enabled", contact_jid);
conf_string_list_add(accounts, sanitized_account_name, "ox.enabled", contact_jid);
} else {
conf_string_list_remove(accounts, account_name, "ox.enabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "ox.enabled", contact_jid);
}
_save_accounts();
_accounts_save(account_name);
}
}
void
accounts_add_pgp_state(const char* const account_name, const char* const contact_jid, gboolean enabled)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
if (enabled) {
conf_string_list_add(accounts, account_name, "pgp.enabled", contact_jid);
conf_string_list_remove(accounts, account_name, "pgp.disabled", contact_jid);
conf_string_list_add(accounts, sanitized_account_name, "pgp.enabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "pgp.disabled", contact_jid);
} else {
conf_string_list_add(accounts, account_name, "pgp.disabled", contact_jid);
conf_string_list_remove(accounts, account_name, "pgp.enabled", contact_jid);
conf_string_list_add(accounts, sanitized_account_name, "pgp.disabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "pgp.enabled", contact_jid);
}
_save_accounts();
_accounts_save(account_name);
}
}
void
accounts_clear_omemo_state(const char* const account_name, const char* const contact_jid)
{
if (accounts_account_exists(account_name)) {
conf_string_list_remove(accounts, account_name, "omemo.enabled", contact_jid);
conf_string_list_remove(accounts, account_name, "omemo.disabled", contact_jid);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
conf_string_list_remove(accounts, sanitized_account_name, "omemo.enabled", contact_jid);
conf_string_list_remove(accounts, sanitized_account_name, "omemo.disabled", contact_jid);
_accounts_save(account_name);
}
}
@@ -779,7 +766,8 @@ accounts_set_priority_dnd(const char* const account_name, const gint value)
void
accounts_set_priority_all(const char* const account_name, const gint value)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
accounts_set_priority_online(account_name, value);
accounts_set_priority_chat(account_name, value);
accounts_set_priority_away(account_name, value);
@@ -793,22 +781,23 @@ accounts_get_priority_for_presence_type(const char* const account_name,
resource_presence_t presence_type)
{
gint result;
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
switch (presence_type) {
case (RESOURCE_ONLINE):
result = g_key_file_get_integer(accounts, account_name, "priority.online", NULL);
result = g_key_file_get_integer(accounts, sanitized_account_name, "priority.online", NULL);
break;
case (RESOURCE_CHAT):
result = g_key_file_get_integer(accounts, account_name, "priority.chat", NULL);
result = g_key_file_get_integer(accounts, sanitized_account_name, "priority.chat", NULL);
break;
case (RESOURCE_AWAY):
result = g_key_file_get_integer(accounts, account_name, "priority.away", NULL);
result = g_key_file_get_integer(accounts, sanitized_account_name, "priority.away", NULL);
break;
case (RESOURCE_XA):
result = g_key_file_get_integer(accounts, account_name, "priority.xa", NULL);
result = g_key_file_get_integer(accounts, sanitized_account_name, "priority.xa", NULL);
break;
default:
result = g_key_file_get_integer(accounts, account_name, "priority.dnd", NULL);
result = g_key_file_get_integer(accounts, sanitized_account_name, "priority.dnd", NULL);
break;
}
@@ -833,7 +822,8 @@ accounts_set_last_status(const char* const account_name, const char* const value
void
accounts_set_last_activity(const char* const account_name)
{
if (accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
GDateTime* nowdt = g_date_time_new_now_utc();
GTimeVal nowtv;
gboolean res = g_date_time_to_timeval(nowdt, &nowtv);
@@ -841,8 +831,8 @@ accounts_set_last_activity(const char* const account_name)
if (res) {
auto_char char* timestr = g_time_val_to_iso8601(&nowtv);
g_key_file_set_string(accounts, account_name, "last.activity", timestr);
_save_accounts();
g_key_file_set_string(accounts, sanitized_account_name, "last.activity", timestr);
_accounts_save(account_name);
}
}
}
@@ -850,8 +840,9 @@ accounts_set_last_activity(const char* const account_name)
gchar*
accounts_get_last_activity(const char* const account_name)
{
if (accounts_account_exists(account_name)) {
return g_key_file_get_string(accounts, account_name, "last.activity", NULL);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
return g_key_file_get_string(accounts, sanitized_account_name, "last.activity", NULL);
} else {
return NULL;
}
@@ -860,27 +851,30 @@ accounts_get_last_activity(const char* const account_name)
gchar*
accounts_get_resource(const char* const account_name)
{
if (!accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (!_accounts_has_group(sanitized_account_name)) {
return NULL;
}
return g_key_file_get_string(accounts, account_name, "resource", NULL);
return g_key_file_get_string(accounts, sanitized_account_name, "resource", NULL);
}
int
accounts_get_max_sessions(const char* const account_name)
{
if (!accounts_account_exists(account_name)) {
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (!_accounts_has_group(sanitized_account_name)) {
return 0;
}
return g_key_file_get_integer(accounts, account_name, "max.sessions", 0);
return g_key_file_get_integer(accounts, sanitized_account_name, "max.sessions", 0);
}
void
accounts_set_login_presence(const char* const account_name, const char* const value)
{
if (accounts_account_exists(account_name)) {
g_key_file_set_string(accounts, account_name, "presence.login", value);
_save_accounts();
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
if (_accounts_has_group(sanitized_account_name)) {
g_key_file_set_string(accounts, sanitized_account_name, "presence.login", value);
_accounts_save(account_name);
}
}
@@ -888,7 +882,8 @@ resource_presence_t
accounts_get_last_presence(const char* const account_name)
{
resource_presence_t result;
auto_gchar gchar* setting = g_key_file_get_string(accounts, account_name, "presence.last", NULL);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
auto_gchar gchar* setting = g_key_file_get_string(accounts, sanitized_account_name, "presence.last", NULL);
if (setting == NULL || (strcmp(setting, "online") == 0)) {
result = RESOURCE_ONLINE;
@@ -912,14 +907,16 @@ accounts_get_last_presence(const char* const account_name)
char*
accounts_get_last_status(const char* const account_name)
{
return g_key_file_get_string(accounts, account_name, "presence.laststatus", NULL);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
return g_key_file_get_string(accounts, sanitized_account_name, "presence.laststatus", NULL);
}
resource_presence_t
accounts_get_login_presence(const char* const account_name)
{
resource_presence_t result;
auto_gchar gchar* setting = g_key_file_get_string(accounts, account_name, "presence.login", NULL);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
auto_gchar gchar* setting = g_key_file_get_string(accounts, sanitized_account_name, "presence.login", NULL);
if (setting == NULL || (strcmp(setting, "online") == 0)) {
result = RESOURCE_ONLINE;
@@ -944,7 +941,8 @@ accounts_get_login_presence(const char* const account_name)
char*
accounts_get_login_status(const char* const account_name)
{
auto_gchar gchar* setting = g_key_file_get_string(accounts, account_name, "presence.login", NULL);
auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name);
auto_gchar gchar* setting = g_key_file_get_string(accounts, sanitized_account_name, "presence.login", NULL);
gchar* status = NULL;
if (g_strcmp0(setting, "last") == 0) {
@@ -953,9 +951,3 @@ accounts_get_login_status(const char* const account_name)
return status;
}
static void
_save_accounts(void)
{
save_keyfile(&accounts_prof_keyfile);
}

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_ACCOUNTS_H
@@ -48,7 +22,7 @@ char* accounts_find_enabled(const char* const prefix, gboolean previous, void* c
void accounts_reset_all_search(void);
void accounts_reset_enabled_search(void);
void accounts_add(const char* jid, const char* altdomain, const int port, const char* const tls_policy, const char* const auth_policy);
int accounts_remove(const char* jid);
gboolean accounts_remove(const char* jid);
gchar** accounts_get_list(void);
ProfAccount* accounts_get_account(const char* const name);
gboolean accounts_enable(const char* const name);

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -53,8 +27,7 @@ _cafile_name(void)
if (!create_dir(certs_dir)) {
return NULL;
}
gchar* filename = g_strdup_printf("%s/CAfile.pem", certs_dir);
return filename;
return g_strdup_printf("%s/CAfile.pem", certs_dir);
}
void
@@ -70,21 +43,25 @@ cafile_add(const TLSCertificate* cert)
auto_gchar gchar* contents = NULL;
auto_gchar gchar* new_contents = NULL;
gsize length;
GError* glib_error = NULL;
auto_gerror GError* glib_error = NULL;
if (g_file_test(cafile, G_FILE_TEST_EXISTS)) {
if (!g_file_get_contents(cafile, &contents, &length, &glib_error)) {
log_error("[CAfile] could not read from %s: %s", cafile, glib_error ? glib_error->message : "No GLib error given");
log_error("[CAfile] could not read from %s: %s", cafile, PROF_GERROR_MESSAGE(glib_error));
return;
}
if (strstr(contents, cert->fingerprint)) {
log_debug("[CAfile] fingerprint %s already stored", cert->fingerprint);
return;
}
if (strstr(contents, cert->fingerprint_sha1)) {
log_debug("[CAfile] fingerprint %s already stored", cert->fingerprint_sha1);
return;
}
}
const char* header = "# Profanity CAfile\n# DO NOT EDIT - this file is automatically generated";
new_contents = g_strdup_printf("%s\n\n# %s\n%s", contents ? contents : header, cert->fingerprint, cert->pem);
new_contents = g_strdup_printf("%s\n# %s\n%s", contents ? contents : header, cert->fingerprint, cert->pem);
if (!g_file_set_contents(cafile, new_contents, -1, &glib_error))
log_error("[CAfile] could not write to %s: %s", cafile, glib_error ? glib_error->message : "No GLib error given");
log_error("[CAfile] could not write to %s: %s", cafile, PROF_GERROR_MESSAGE(glib_error));
}
gchar*

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_CAFILE_H

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2019 Aurelien Aptel <aurelien.aptel@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -59,7 +33,7 @@ static struct color_pair_cache
{
struct
{
int16_t fg, bg;
short fg, bg;
}* pairs;
int size;
int capacity;
@@ -341,14 +315,18 @@ color_distance(const struct color_def* a, const struct color_def* b)
return h * h + s * s + l * l;
}
static int
/* Indices into the 256-colour palette and ncurses palette IDs are
* 16-bit (the size_t-or-short distinction in ncurses init_pair).
* Use `short` end-to-end so the caller boundaries don't need casts.
*/
static short
find_closest_col(int h, int s, int l)
{
struct color_def a = { h, s, l };
int min = 0;
struct color_def a = { (uint16_t)h, (uint8_t)s, (uint8_t)l, NULL };
short min = 0;
int dmin = color_distance(&a, &color_names[0]);
for (int i = 1; i < COLOR_NAME_SIZE; i++) {
for (short i = 1; i < COLOR_NAME_SIZE; i++) {
int d = color_distance(&a, &color_names[i]);
if (d < dmin) {
dmin = d;
@@ -358,8 +336,8 @@ find_closest_col(int h, int s, int l)
return min;
}
static int
find_col(const char* col_name, int n)
static short
find_col(const char* col_name, size_t n)
{
char name[32] = { 0 };
@@ -371,7 +349,7 @@ find_col(const char* col_name, int n)
if (n >= sizeof(name)) {
/* truncate */
log_error("Color: <%s,%d> bigger than %zu", col_name, n, sizeof(name));
log_error("Color: <%s,%zu> bigger than %zu", col_name, n, sizeof(name));
n = sizeof(name) - 1;
}
memcpy(name, col_name, n);
@@ -380,7 +358,7 @@ find_col(const char* col_name, int n)
return -1;
}
for (int i = 0; i < COLOR_NAME_SIZE; i++) {
for (short i = 0; i < COLOR_NAME_SIZE; i++) {
if (g_ascii_strcasecmp(name, color_names[i].name) == 0) {
return i;
}
@@ -389,13 +367,13 @@ find_col(const char* col_name, int n)
return COL_ERR;
}
static int
static short
color_hash(const char* str, color_profile profile)
{
GChecksum* cs = NULL;
guint8 buf[256] = { 0 };
gsize len = 256;
int rc = -1; /* default ncurse color */
short rc = -1; /* default ncurse color */
cs = g_checksum_new(G_CHECKSUM_SHA1);
if (!cs)
@@ -464,7 +442,7 @@ color_pair_cache_reset(void)
}
static int
_color_pair_cache_get(int fg, int bg)
_color_pair_cache_get(short fg, short bg)
{
if (COLORS < 256) {
if (fg > 7 || bg > 7) {
@@ -489,10 +467,13 @@ _color_pair_cache_get(int fg, int bg)
}
int i = cache.size;
// TODO: init_pair uses 15-bit palette indices. Terminals exposing
// extended colour palettes (>32767) need init_extended_pair here.
// Upstream-inherited limitation.
cache.pairs[i].fg = fg;
cache.pairs[i].bg = bg;
/* (re-)define the new pair in curses */
init_pair(i, fg, bg);
init_pair((short)i, fg, bg);
cache.size++;
@@ -511,8 +492,8 @@ _color_pair_cache_get(int fg, int bg)
int
color_pair_cache_hash_str(const char* str, color_profile profile)
{
int fg = color_hash(str, profile);
int bg = -1;
short fg = color_hash(str, profile);
short bg = -1;
auto_char char* bkgnd = theme_get_bkgnd();
if (bkgnd) {
@@ -532,7 +513,7 @@ int
color_pair_cache_get(const char* pair_name)
{
const char* sep;
int fg, bg;
short fg, bg;
sep = strchr(pair_name, '_');
if (!sep) {
@@ -540,7 +521,7 @@ color_pair_cache_get(const char* pair_name)
return -1;
}
fg = find_col(pair_name, sep - pair_name);
fg = find_col(pair_name, (size_t)(sep - pair_name));
bg = find_col(sep + 1, strlen(sep));
if (fg == COL_ERR || bg == COL_ERR) {
log_error("Color: bad color name %s", pair_name);

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2019 Aurelien Aptel <aurelien.aptel@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef _COLOR_H_

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -66,9 +40,7 @@ conf_string_list_add(GKeyFile* keyfile, const char* const group, const char* con
// Add item to the existing list
const gchar** new_list = g_new(const gchar*, length + 2);
for (gsize i = 0; i < length; ++i) {
new_list[i] = list[i];
}
memcpy(new_list, list, sizeof(list[0]) * length);
new_list[length] = item;
new_list[length + 1] = NULL;
@@ -89,33 +61,28 @@ conf_string_list_remove(GKeyFile* keyfile, const char* const group, const char*
return FALSE;
}
GList* glist = NULL;
gsize new_length = 0;
const gchar** new_list = g_new(const gchar*, length + 1);
gboolean deleted = FALSE;
for (int i = 0; i < length; i++) {
for (gsize i = 0; i < length; i++) {
if (strcmp(list[i], item) == 0) {
deleted = TRUE;
continue;
}
glist = g_list_append(glist, strdup(list[i]));
new_list[new_length++] = list[i];
}
new_list[new_length] = NULL;
if (deleted) {
if (g_list_length(glist) == 0) {
if (new_length == 0) {
g_key_file_remove_key(keyfile, group, key, NULL);
} else {
const gchar* new_list[g_list_length(glist) + 1];
int i = 0;
for (GList* curr = glist; curr; curr = g_list_next(curr)) {
new_list[i++] = curr->data;
}
new_list[i] = NULL;
g_key_file_set_string_list(keyfile, group, key, new_list, g_list_length(glist));
g_key_file_set_string_list(keyfile, group, key, new_list, new_length);
}
}
g_list_free_full(glist, g_free);
g_free(new_list);
return deleted;
}

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_CONFLISTS_H

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2020 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2020 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2018 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2018 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_FILES_H

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -49,6 +23,7 @@
#include "tools/autocomplete.h"
#include "config/files.h"
#include "config/conflists.h"
#include "ui/ui.h"
// preference groups refer to the sections in .profrc or theme files
// for example [ui] but not [colours] which is handled in theme.c
@@ -66,6 +41,7 @@
#define PREF_GROUP_MUC "muc"
#define PREF_GROUP_PLUGINS "plugins"
#define PREF_GROUP_EXECUTABLES "executables"
#define PREF_GROUP_SPELLCHECK "spellcheck"
#define PREF_GROUP_AI "ai"
#define PREF_GROUP_AI_PREFIX "ai/" /* per-provider subsection prefix: [ai/<provider>] */
#define PREF_AI_SETTING_PREFIX "setting." /* per-provider custom setting key prefix */
@@ -80,10 +56,10 @@ static Autocomplete boolean_choice_ac;
static Autocomplete room_trigger_ac;
static void _save_prefs(void);
static const char* _get_group(preference_t pref);
static const char* _get_key(preference_t pref);
static const gchar* _get_group(preference_t pref);
static const gchar* _get_key(preference_t pref);
static gboolean _get_default_boolean(preference_t pref);
static char* _get_default_string(preference_t pref);
static gchar* _get_default_string(preference_t pref);
static void
_prefs_load(void)
@@ -92,7 +68,7 @@ _prefs_load(void)
log_maxsize = g_key_file_get_integer(prefs, PREF_GROUP_LOGGING, "maxsize", &err);
if (err) {
log_maxsize = 0;
g_error_free(err);
PROF_GERROR_FREE(err);
}
// move pre 0.5.0 autoaway.time to autoaway.awaytime
@@ -112,7 +88,7 @@ _prefs_load(void)
// migrate pre 0.5.0 time settings
if (g_key_file_has_key(prefs, PREF_GROUP_UI, "time", NULL)) {
auto_gchar gchar* time = g_key_file_get_string(prefs, PREF_GROUP_UI, "time", NULL);
char* val = time ? time : "off";
gchar* val = time ? time : "off";
g_key_file_set_string(prefs, PREF_GROUP_UI, "time.console", val);
g_key_file_set_string(prefs, PREF_GROUP_UI, "time.chat", val);
g_key_file_set_string(prefs, PREF_GROUP_UI, "time.muc", val);
@@ -181,7 +157,7 @@ _prefs_load(void)
if (values && !g_key_file_has_key(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", NULL)) {
// First value in array is `require_save` option -- we ignore that
// one as there is no such option anymore.
char* executable = values[1];
gchar* executable = values[1];
g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", executable);
g_key_file_set_comment(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", " Migrated from url.open.cmd[DEF]. `require_save` option has been removed in v0.10 and was discarded.", NULL);
@@ -318,7 +294,7 @@ _prefs_load(void)
gsize len = 0;
auto_gcharv gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
for (int i = 0; i < len; i++) {
for (gsize i = 0; i < len; i++) {
autocomplete_add(room_trigger_ac, triggers[i]);
}
}
@@ -340,7 +316,7 @@ prefs_reload(void)
}
void
prefs_load(const char* config_file)
prefs_load(const gchar* config_file)
{
if (config_file == NULL) {
load_config_keyfile(&prefs_prof_keyfile, FILE_PROFRC);
@@ -352,6 +328,77 @@ prefs_load(const char* config_file)
_prefs_load();
}
static void
prefs_changes_print(const gchar* key, const gchar* newval, const gchar* oldval, gboolean* banner_shown)
{
#define PREFS_CHANGES_FORMAT_STRING "%-32s | %-20s | %-20s"
if (!*banner_shown) {
cons_show(PREFS_CHANGES_FORMAT_STRING, "Key", "New value", "Old value");
*banner_shown = TRUE;
}
cons_show(PREFS_CHANGES_FORMAT_STRING, key, newval, oldval);
#undef PREFS_CHANGES_FORMAT_STRING
}
void
prefs_changes(void)
{
const gchar* undef = "";
prof_keyfile_t saved;
if (!load_custom_keyfile(&saved, g_strdup(prefs_prof_keyfile.filename)))
return;
gsize ngroups, nsavedgroups, g;
auto_gcharv gchar** groups = g_key_file_get_groups(prefs_prof_keyfile.keyfile, &ngroups);
gboolean banner_shown = FALSE;
for (g = 0; g < ngroups; ++g) {
gsize nkeys, k;
gboolean saved_has_group = g_key_file_has_group(saved.keyfile, groups[g]);
auto_gcharv gchar** keys = g_key_file_get_keys(prefs_prof_keyfile.keyfile, groups[g], &nkeys, NULL);
for (k = 0; k < nkeys; ++k) {
auto_gerror GError* err = NULL;
auto_gchar gchar* full_key = g_strdup_printf("%s.%s", groups[g], keys[k]);
auto_gchar gchar* new_value = g_key_file_get_value(prefs_prof_keyfile.keyfile, groups[g], keys[k], &err);
if (err || !new_value) {
cons_show_error("%s: New value (%s) error: %s", full_key, STR_MAYBE_NULL(new_value), PROF_GERROR_MESSAGE(err));
continue;
}
if (!saved_has_group
|| !g_key_file_has_key(saved.keyfile, groups[g], keys[k], NULL)) {
prefs_changes_print(full_key, new_value, undef, &banner_shown);
continue;
}
auto_gchar gchar* old_value = g_key_file_get_value(saved.keyfile, groups[g], keys[k], &err);
if (err || !old_value) {
cons_show_error("%s: Old value (%s) error: %s", full_key, STR_MAYBE_NULL(old_value), PROF_GERROR_MESSAGE(err));
continue;
}
if (!g_str_equal(old_value, new_value)) {
prefs_changes_print(full_key, new_value, old_value, &banner_shown);
}
}
}
auto_gcharv gchar** savedgroups = g_key_file_get_groups(saved.keyfile, &nsavedgroups);
for (g = 0; g < nsavedgroups; ++g) {
gsize nkeys, k;
auto_gcharv gchar** keys = g_key_file_get_keys(saved.keyfile, savedgroups[g], &nkeys, NULL);
for (k = 0; k < nkeys; ++k) {
if (g_key_file_has_key(prefs_prof_keyfile.keyfile, savedgroups[g], keys[k], NULL))
continue;
auto_gerror GError* err = NULL;
auto_gchar gchar* full_key = g_strdup_printf("%s.%s", groups[g], keys[k]);
auto_gchar gchar* old_value = g_key_file_get_value(saved.keyfile, groups[g], keys[k], &err);
if (err || !old_value) {
cons_show_error("%s: Old value (%s) error: %s", full_key, STR_MAYBE_NULL(old_value), PROF_GERROR_MESSAGE(err));
continue;
}
prefs_changes_print(full_key, undef, old_value, &banner_shown);
}
}
free_keyfile(&saved);
if (!banner_shown)
cons_show("No changes to saved preferences.");
}
void
prefs_save(void)
{
@@ -368,7 +415,7 @@ prefs_close(void)
}
gchar*
prefs_autocomplete_boolean_choice(const char* const prefix, gboolean previous, void* context)
prefs_autocomplete_boolean_choice(const gchar* const prefix, gboolean previous, void* context)
{
return autocomplete_complete(boolean_choice_ac, prefix, TRUE, previous);
}
@@ -380,7 +427,7 @@ prefs_reset_boolean_choice(void)
}
gchar*
prefs_autocomplete_room_trigger(const char* const prefix, gboolean previous, void* context)
prefs_autocomplete_room_trigger(const gchar* const prefix, gboolean previous, void* context)
{
return autocomplete_complete(room_trigger_ac, prefix, TRUE, previous);
}
@@ -406,7 +453,7 @@ prefs_do_chat_notify(gboolean current_win)
}
GList*
prefs_message_get_triggers(const char* const message)
prefs_message_get_triggers(const gchar* const message)
{
GList* result = NULL;
@@ -414,7 +461,7 @@ prefs_message_get_triggers(const char* const message)
gsize len = 0;
auto_gcharv gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
for (int i = 0; i < len; i++) {
for (gsize i = 0; i < len; i++) {
auto_gchar gchar* trigger_lower = g_utf8_strdown(triggers[i], -1);
if (g_strrstr(message_lower, trigger_lower)) {
result = g_list_append(result, strdup(triggers[i]));
@@ -425,8 +472,8 @@ prefs_message_get_triggers(const char* const message)
}
gboolean
prefs_do_room_notify(gboolean current_win, const char* const roomjid, const char* const mynick,
const char* const theirnick, const char* const message, gboolean mention, gboolean trigger_found)
prefs_do_room_notify(gboolean current_win, const gchar* const roomjid, const gchar* const mynick,
const gchar* const theirnick, const gchar* const message, gboolean mention, gboolean trigger_found)
{
if (g_strcmp0(mynick, theirnick) == 0) {
return FALSE;
@@ -475,7 +522,7 @@ prefs_do_room_notify(gboolean current_win, const char* const roomjid, const char
}
gboolean
prefs_do_room_notify_mention(const char* const roomjid, int unread, gboolean mention, gboolean trigger)
prefs_do_room_notify_mention(const gchar* const roomjid, int unread, gboolean mention, gboolean trigger)
{
gboolean notify_room = FALSE;
if (g_key_file_has_key(prefs, roomjid, "notify", NULL)) {
@@ -511,61 +558,61 @@ prefs_do_room_notify_mention(const char* const roomjid, int unread, gboolean men
}
void
prefs_set_room_notify(const char* const roomjid, gboolean value)
prefs_set_room_notify(const gchar* const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify", value);
}
void
prefs_set_room_notify_mention(const char* const roomjid, gboolean value)
prefs_set_room_notify_mention(const gchar* const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify.mention", value);
}
void
prefs_set_room_notify_trigger(const char* const roomjid, gboolean value)
prefs_set_room_notify_trigger(const gchar* const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify.trigger", value);
}
gboolean
prefs_has_room_notify(const char* const roomjid)
prefs_has_room_notify(const gchar* const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify", NULL);
}
gboolean
prefs_has_room_notify_mention(const char* const roomjid)
prefs_has_room_notify_mention(const gchar* const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify.mention", NULL);
}
gboolean
prefs_has_room_notify_trigger(const char* const roomjid)
prefs_has_room_notify_trigger(const gchar* const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL);
}
gboolean
prefs_get_room_notify(const char* const roomjid)
prefs_get_room_notify(const gchar* const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
}
gboolean
prefs_get_room_notify_mention(const char* const roomjid)
prefs_get_room_notify_mention(const gchar* const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
}
gboolean
prefs_get_room_notify_trigger(const char* const roomjid)
prefs_get_room_notify_trigger(const gchar* const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
}
gboolean
prefs_reset_room_notify(const char* const roomjid)
prefs_reset_room_notify(const gchar* const roomjid)
{
if (g_key_file_has_group(prefs, roomjid)) {
g_key_file_remove_group(prefs, roomjid, NULL);
@@ -578,8 +625,8 @@ prefs_reset_room_notify(const char* const roomjid)
gboolean
prefs_get_boolean(preference_t pref)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
gboolean def = _get_default_boolean(pref);
if (!g_key_file_has_key(prefs, group, key, NULL)) {
@@ -592,8 +639,8 @@ prefs_get_boolean(preference_t pref)
void
prefs_set_boolean(preference_t pref, gboolean value)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
g_key_file_set_boolean(prefs, group, key, value);
}
@@ -608,9 +655,9 @@ prefs_set_boolean(preference_t pref, gboolean value)
gchar*
prefs_get_string(preference_t pref)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
char* def = _get_default_string(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
gchar* def = _get_default_string(pref);
gchar* result = g_key_file_get_string(prefs, group, key, NULL);
@@ -637,9 +684,9 @@ prefs_get_string(preference_t pref)
gchar*
prefs_get_string_with_locale(preference_t pref, gchar* locale)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
char* def = _get_default_string(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
gchar* def = _get_default_string(pref);
gchar* result = g_key_file_get_locale_string(prefs, group, key, locale, NULL);
@@ -668,8 +715,8 @@ prefs_get_string_with_locale(preference_t pref, gchar* locale)
void
prefs_set_string(preference_t pref, const gchar* new_value)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
if (new_value == NULL) {
g_key_file_remove_key(prefs, group, key, NULL);
} else {
@@ -678,10 +725,10 @@ prefs_set_string(preference_t pref, const gchar* new_value)
}
void
prefs_set_string_with_option(preference_t pref, char* option, char* value)
prefs_set_string_with_option(preference_t pref, gchar* option, gchar* value)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
if (value == NULL) {
g_key_file_remove_key(prefs, group, key, NULL);
} else {
@@ -690,10 +737,10 @@ prefs_set_string_with_option(preference_t pref, char* option, char* value)
}
void
prefs_set_string_list_with_option(preference_t pref, char* option, const gchar* const* values)
prefs_set_string_list_with_option(preference_t pref, gchar* option, const gchar* const* values)
{
const char* group = _get_group(pref);
const char* key = _get_key(pref);
const gchar* group = _get_group(pref);
const gchar* key = _get_key(pref);
if (values == NULL || *values == NULL) {
if (g_strcmp0(option, "*") == 0) {
g_key_file_set_string_list(prefs, group, key, NULL, 0);
@@ -713,11 +760,11 @@ prefs_set_string_list_with_option(preference_t pref, char* option, const gchar*
}
}
char*
gchar*
prefs_get_tls_certpath(void)
{
const char* group = _get_group(PREF_TLS_CERTPATH);
const char* key = _get_key(PREF_TLS_CERTPATH);
const gchar* group = _get_group(PREF_TLS_CERTPATH);
const gchar* key = _get_key(PREF_TLS_CERTPATH);
auto_gchar gchar* setting = g_key_file_get_string(prefs, group, key, NULL);
@@ -745,7 +792,7 @@ prefs_get_tls_certpath(void)
return NULL;
}
char* result = strdup(setting);
gchar* result = g_strdup(setting);
return result;
}
@@ -949,14 +996,14 @@ prefs_get_plugins(void)
}
void
prefs_add_plugin(const char* const name)
prefs_add_plugin(const gchar* const name)
{
conf_string_list_add(prefs, PREF_GROUP_PLUGINS, "load", name);
_save_prefs();
}
void
prefs_remove_plugin(const char* const name)
prefs_remove_plugin(const gchar* const name)
{
conf_string_list_remove(prefs, PREF_GROUP_PLUGINS, "load", name);
_save_prefs();
@@ -990,7 +1037,7 @@ prefs_get_occupants_char(void)
}
void
prefs_set_occupants_char(char* ch)
prefs_set_occupants_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "occupants.char", ch);
@@ -1035,7 +1082,7 @@ prefs_get_occupants_header_char(void)
}
void
prefs_set_occupants_header_char(char* ch)
prefs_set_occupants_header_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "occupants.header.char", ch);
@@ -1069,7 +1116,7 @@ prefs_get_roster_size(void)
}
static gchar*
_prefs_get_encryption_char(const char* const ch, const char* const pref_group, const char* const key)
_prefs_get_encryption_char(const gchar* const ch, const gchar* const pref_group, const gchar* const key)
{
gchar* result = NULL;
@@ -1084,7 +1131,7 @@ _prefs_get_encryption_char(const char* const ch, const char* const pref_group, c
}
static gboolean
_prefs_set_encryption_char(const char* const ch, const char* const pref_group, const char* const key)
_prefs_set_encryption_char(const gchar* const ch, const gchar* const pref_group, const gchar* const key)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, pref_group, key, ch);
@@ -1102,7 +1149,7 @@ prefs_get_otr_char(void)
}
gboolean
prefs_set_otr_char(char* ch)
prefs_set_otr_char(gchar* ch)
{
return _prefs_set_encryption_char(ch, PREF_GROUP_OTR, "otr.char");
}
@@ -1114,7 +1161,7 @@ prefs_get_pgp_char(void)
}
gboolean
prefs_set_pgp_char(char* ch)
prefs_set_pgp_char(gchar* ch)
{
return _prefs_set_encryption_char(ch, PREF_GROUP_PGP, "pgp.char");
}
@@ -1126,7 +1173,7 @@ prefs_get_ox_char(void)
}
gboolean
prefs_set_ox_char(char* ch)
prefs_set_ox_char(gchar* ch)
{
return _prefs_set_encryption_char(ch, PREF_GROUP_OX, "ox.char");
}
@@ -1138,7 +1185,7 @@ prefs_get_omemo_char(void)
}
gboolean
prefs_set_omemo_char(char* ch)
prefs_set_omemo_char(gchar* ch)
{
return _prefs_set_encryption_char(ch, PREF_GROUP_OMEMO, "omemo.char");
}
@@ -1150,7 +1197,7 @@ prefs_get_roster_header_char(void)
}
void
prefs_set_roster_header_char(char* ch)
prefs_set_roster_header_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.header.char", ch);
@@ -1172,7 +1219,7 @@ prefs_get_roster_contact_char(void)
}
void
prefs_set_roster_contact_char(char* ch)
prefs_set_roster_contact_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.contact.char", ch);
@@ -1194,7 +1241,7 @@ prefs_get_roster_resource_char(void)
}
void
prefs_set_roster_resource_char(char* ch)
prefs_set_roster_resource_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.resource.char", ch);
@@ -1216,7 +1263,7 @@ prefs_get_roster_private_char(void)
}
void
prefs_set_roster_private_char(char* ch)
prefs_set_roster_private_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.private.char", ch);
@@ -1238,7 +1285,7 @@ prefs_get_roster_room_char(void)
}
void
prefs_set_roster_room_char(char* ch)
prefs_set_roster_room_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.rooms.char", ch);
@@ -1260,7 +1307,7 @@ prefs_get_roster_room_private_char(void)
}
void
prefs_set_roster_room_private_char(char* ch)
prefs_set_roster_room_private_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.rooms.private.char", ch);
@@ -1347,7 +1394,7 @@ prefs_get_correction_char(void)
}
void
prefs_set_correction_char(char* ch)
prefs_set_correction_char(gchar* ch)
{
if (g_utf8_strlen(ch, 4) == 1) {
g_key_file_set_string(prefs, PREF_GROUP_UI, "correction.char", ch);
@@ -1357,7 +1404,7 @@ prefs_set_correction_char(char* ch)
}
gboolean
prefs_add_room_notify_trigger(const char* const text)
prefs_add_room_notify_trigger(const gchar* const text)
{
gboolean res = conf_string_list_add(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
@@ -1369,7 +1416,7 @@ prefs_add_room_notify_trigger(const char* const text)
}
gboolean
prefs_remove_room_notify_trigger(const char* const text)
prefs_remove_room_notify_trigger(const gchar* const text)
{
gboolean res = conf_string_list_remove(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
_save_prefs();
@@ -1388,7 +1435,7 @@ prefs_get_room_notify_triggers(void)
gsize len = 0;
auto_gcharv gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
for (int i = 0; i < len; i++) {
for (gsize i = 0; i < len; i++) {
result = g_list_append(result, strdup(triggers[i]));
}
@@ -1398,7 +1445,7 @@ prefs_get_room_notify_triggers(void)
ProfWinPlacement*
prefs_create_profwin_placement(int titlebar, int mainwin, int statusbar, int inputwin)
{
ProfWinPlacement* placement = malloc(sizeof(ProfWinPlacement));
ProfWinPlacement* placement = g_new0(ProfWinPlacement, 1);
placement->titlebar_pos = titlebar;
placement->mainwin_pos = mainwin;
placement->statusbar_pos = statusbar;
@@ -1689,7 +1736,7 @@ prefs_inputwin_pos_down(void)
}
gboolean
prefs_add_alias(const char* const name, const char* const value)
prefs_add_alias(const gchar* const name, const gchar* const value)
{
if (g_key_file_has_key(prefs, PREF_GROUP_ALIAS, name, NULL)) {
return FALSE;
@@ -1700,13 +1747,13 @@ prefs_add_alias(const char* const name, const char* const value)
}
gchar*
prefs_get_alias(const char* const name)
prefs_get_alias(const gchar* const name)
{
return g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
}
gboolean
prefs_remove_alias(const char* const name)
prefs_remove_alias(const gchar* const name)
{
if (!g_key_file_has_key(prefs, PREF_GROUP_ALIAS, name, NULL)) {
return FALSE;
@@ -1735,14 +1782,14 @@ prefs_get_aliases(void)
gsize len;
auto_gcharv gchar** keys = g_key_file_get_keys(prefs, PREF_GROUP_ALIAS, &len, NULL);
for (int i = 0; i < len; i++) {
char* name = keys[i];
for (gsize i = 0; i < len; i++) {
gchar* name = keys[i];
auto_gchar gchar* value = g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
if (value) {
ProfAlias* alias = malloc(sizeof(struct prof_alias_t));
alias->name = strdup(name);
alias->value = strdup(value);
ProfAlias* alias = g_new0(struct prof_alias_t, 1);
alias->name = g_strdup(name);
alias->value = g_strdup(value);
result = g_list_insert_sorted(result, alias, (GCompareFunc)_alias_cmp);
}
@@ -1755,9 +1802,9 @@ prefs_get_aliases(void)
void
_free_alias(ProfAlias* alias)
{
FREE_SET_NULL(alias->name);
FREE_SET_NULL(alias->value);
FREE_SET_NULL(alias);
GFREE_SET_NULL(alias->name);
GFREE_SET_NULL(alias->value);
GFREE_SET_NULL(alias);
}
void
@@ -2135,7 +2182,7 @@ prefs_free_ai_settings(GList* settings)
// get the preference group for a specific preference
// for example the PREF_BEEP setting ("beep" in .profrc, see _get_key) belongs
// to the [ui] section.
static const char*
static const gchar*
_get_group(preference_t pref)
{
switch (pref) {
@@ -2292,6 +2339,9 @@ _get_group(preference_t pref)
return PREF_GROUP_OMEMO;
case PREF_OX_LOG:
return PREF_GROUP_OX;
case PREF_SPELLCHECK_ENABLE:
case PREF_SPELLCHECK_LANG:
return PREF_GROUP_SPELLCHECK;
case PREF_AI_PROVIDER:
case PREF_AI_API_KEY:
return PREF_GROUP_AI;
@@ -2302,7 +2352,7 @@ _get_group(preference_t pref)
// get the key used in .profrc for the preference
// for example the PREF_AUTOAWAY_MODE maps to "autoaway.mode" in .profrc
static const char*
static const gchar*
_get_key(preference_t pref)
{
switch (pref) {
@@ -2592,6 +2642,10 @@ _get_key(preference_t pref)
return "force-encryption.enabled";
case PREF_FORCE_ENCRYPTION_MODE:
return "force-encryption.policy";
case PREF_SPELLCHECK_ENABLE:
return "enabled";
case PREF_SPELLCHECK_LANG:
return "lang";
default:
return NULL;
}
@@ -2646,6 +2700,7 @@ _get_default_boolean(preference_t pref)
case PREF_STROPHE_SM_ENABLED:
case PREF_STROPHE_SM_RESEND:
return TRUE;
case PREF_SPELLCHECK_ENABLE:
case PREF_PGP_PUBKEY_AUTOIMPORT:
case PREF_FORCE_ENCRYPTION:
default:
@@ -2655,7 +2710,7 @@ _get_default_boolean(preference_t pref)
// the default setting for a string type preference
// if it is not specified in .profrc
static char*
static gchar*
_get_default_string(preference_t pref)
{
switch (pref) {
@@ -2756,6 +2811,8 @@ _get_default_string(preference_t pref)
return "on";
case PREF_FORCE_ENCRYPTION_MODE:
return "resend-to-confirm";
case PREF_SPELLCHECK_LANG:
return "en_US";
case PREF_AI_PROVIDER:
return "openai";
case PREF_AI_API_KEY:

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_PREFERENCES_H
@@ -192,7 +166,9 @@ typedef enum {
PREF_VCARD_PHOTO_CMD,
PREF_STATUSBAR_TABMODE,
PREF_FORCE_ENCRYPTION,
PREF_FORCE_ENCRYPTION_MODE
PREF_FORCE_ENCRYPTION_MODE,
PREF_SPELLCHECK_ENABLE,
PREF_SPELLCHECK_LANG,
} preference_t;
typedef struct prof_alias_t
@@ -209,15 +185,16 @@ typedef struct prof_winplacement_t
int inputwin_pos;
} ProfWinPlacement;
void prefs_load(const char* config_file);
void prefs_load(const gchar* config_file);
void prefs_changes(void);
void prefs_save(void);
void prefs_close(void);
void prefs_reload(void);
gchar* prefs_autocomplete_boolean_choice(const char* const prefix, gboolean previous, void* context);
gchar* prefs_autocomplete_boolean_choice(const gchar* const prefix, gboolean previous, void* context);
void prefs_reset_boolean_choice(void);
gchar* prefs_autocomplete_room_trigger(const char* const prefix, gboolean previous, void* context);
gchar* prefs_autocomplete_room_trigger(const gchar* const prefix, gboolean previous, void* context);
void prefs_reset_room_trigger_ac(void);
gint prefs_get_gone(void);
@@ -254,42 +231,42 @@ gint prefs_get_autoxa_time(void);
void prefs_set_autoxa_time(gint value);
gchar** prefs_get_plugins(void);
void prefs_add_plugin(const char* const name);
void prefs_remove_plugin(const char* const name);
void prefs_add_plugin(const gchar* const name);
void prefs_remove_plugin(const gchar* const name);
gchar* prefs_get_otr_char(void);
gboolean prefs_set_otr_char(char* ch);
gboolean prefs_set_otr_char(gchar* ch);
gchar* prefs_get_pgp_char(void);
gboolean prefs_set_pgp_char(char* ch);
gboolean prefs_set_pgp_char(gchar* ch);
gchar* prefs_get_omemo_char(void);
gboolean prefs_set_omemo_char(char* ch);
gboolean prefs_set_omemo_char(gchar* ch);
// XEP-0373: OpenPGP for XMPP
char* prefs_get_ox_char(void);
gboolean prefs_set_ox_char(char* ch);
gboolean prefs_set_ox_char(gchar* ch);
gchar* prefs_get_roster_header_char(void);
void prefs_set_roster_header_char(char* ch);
void prefs_set_roster_header_char(gchar* ch);
void prefs_clear_roster_header_char(void);
gchar* prefs_get_roster_contact_char(void);
void prefs_set_roster_contact_char(char* ch);
void prefs_set_roster_contact_char(gchar* ch);
void prefs_clear_roster_contact_char(void);
gchar* prefs_get_roster_resource_char(void);
void prefs_set_roster_resource_char(char* ch);
void prefs_set_roster_resource_char(gchar* ch);
void prefs_clear_roster_resource_char(void);
gchar* prefs_get_roster_private_char(void);
void prefs_set_roster_private_char(char* ch);
void prefs_set_roster_private_char(gchar* ch);
void prefs_clear_roster_private_char(void);
gchar* prefs_get_roster_room_char(void);
void prefs_set_roster_room_char(char* ch);
void prefs_set_roster_room_char(gchar* ch);
void prefs_clear_roster_room_char(void);
gchar* prefs_get_roster_room_private_char(void);
void prefs_set_roster_room_private_char(char* ch);
void prefs_set_roster_room_private_char(gchar* ch);
void prefs_clear_roster_room_private_char(void);
gchar* prefs_get_occupants_char(void);
void prefs_set_occupants_char(char* ch);
void prefs_set_occupants_char(gchar* ch);
void prefs_clear_occupants_char(void);
gchar* prefs_get_occupants_header_char(void);
void prefs_set_occupants_header_char(char* ch);
void prefs_set_occupants_header_char(gchar* ch);
void prefs_clear_occupants_header_char(void);
gint prefs_get_roster_contact_indent(void);
@@ -302,21 +279,21 @@ gint prefs_get_occupants_indent(void);
void prefs_set_occupants_indent(gint value);
gchar* prefs_get_correction_char(void);
void prefs_set_correction_char(char* ch);
void prefs_set_correction_char(gchar* ch);
void prefs_add_login(const char* jid);
void prefs_add_login(const gchar* jid);
void prefs_set_tray_timer(gint value);
gint prefs_get_tray_timer(void);
gboolean prefs_add_alias(const char* const name, const char* const value);
gboolean prefs_remove_alias(const char* const name);
gchar* prefs_get_alias(const char* const name);
gboolean prefs_add_alias(const gchar* const name, const gchar* const value);
gboolean prefs_remove_alias(const gchar* const name);
gchar* prefs_get_alias(const gchar* const name);
GList* prefs_get_aliases(void);
void prefs_free_aliases(GList* aliases);
gboolean prefs_add_room_notify_trigger(const char* const text);
gboolean prefs_remove_room_notify_trigger(const char* const text);
gboolean prefs_add_room_notify_trigger(const gchar* const text);
gboolean prefs_remove_room_notify_trigger(const gchar* const text);
GList* prefs_get_room_notify_triggers(void);
ProfWinPlacement* prefs_get_win_placement(void);

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_SCRIPTS_H

View File

@@ -3,35 +3,9 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -117,6 +91,7 @@ theme_init(const char* const theme_name)
g_hash_table_insert(defaults, strdup("mention"), strdup("yellow"));
g_hash_table_insert(defaults, strdup("trigger"), strdup("yellow"));
g_hash_table_insert(defaults, strdup("input.text"), strdup("default"));
g_hash_table_insert(defaults, strdup("input.misspelled"), strdup("red"));
g_hash_table_insert(defaults, strdup("main.time"), strdup("default"));
g_hash_table_insert(defaults, strdup("titlebar.text"), strdup("white"));
g_hash_table_insert(defaults, strdup("titlebar.brackets"), strdup("cyan"));
@@ -533,7 +508,7 @@ _theme_find(const char* const theme_name)
g_string_append(path, "/");
g_string_append(path, theme_name);
if (!g_file_test(path->str, G_FILE_TEST_EXISTS)) {
g_string_free(path, true);
g_string_free(path, TRUE);
path = NULL;
}
}
@@ -667,25 +642,17 @@ _theme_prep_fgnd(char* setting, GString* lookup_str, gboolean* bold)
}
}
char*
gchar*
theme_get_string(char* str)
{
gchar* res = g_key_file_get_string(theme, "colours", str, NULL);
if (!res) {
return strdup(g_hash_table_lookup(defaults, str));
return g_strdup(g_hash_table_lookup(defaults, str));
} else {
return res;
}
}
void
theme_free_string(char* str)
{
if (str) {
g_free(str);
}
}
int
theme_hash_attrs(const char* str)
{
@@ -748,6 +715,9 @@ theme_attrs(theme_item_t attrs)
case THEME_INPUT_TEXT:
_theme_prep_fgnd("input.text", lookup_str, &bold);
break;
case THEME_INPUT_MISSPELLED:
_theme_prep_fgnd("input.misspelled", lookup_str, &bold);
break;
case THEME_TIME:
_theme_prep_fgnd("main.time", lookup_str, &bold);
break;

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_THEME_H
@@ -53,6 +27,7 @@ typedef enum {
THEME_MENTION,
THEME_TRIGGER,
THEME_INPUT_TEXT,
THEME_INPUT_MISSPELLED,
THEME_TIME,
THEME_TITLE_TEXT,
THEME_TITLE_BRACKET,
@@ -152,7 +127,6 @@ void theme_close(void);
int theme_hash_attrs(const char* str);
int theme_attrs(theme_item_t attrs);
char* theme_get_string(char* str);
void theme_free_string(char* str);
theme_item_t theme_main_presence_attrs(const char* const presence);
theme_item_t theme_roster_unread_presence_attrs(const char* const presence);
theme_item_t theme_roster_active_presence_attrs(const char* const presence);

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
@@ -54,7 +28,7 @@ static void _save_tlscerts(void);
static Autocomplete certs_ac;
static char* current_fp;
static gchar* current_fp;
static void
_tlscerts_close(void)
@@ -62,7 +36,7 @@ _tlscerts_close(void)
free_keyfile(&tlscerts_prof_keyfile);
tlscerts = NULL;
free(current_fp);
g_free(current_fp);
current_fp = NULL;
autocomplete_free(certs_ac);
@@ -82,7 +56,7 @@ tlscerts_init(void)
gsize len = 0;
auto_gcharv gchar** groups = g_key_file_get_groups(tlscerts, &len);
for (int i = 0; i < g_strv_length(groups); i++) {
for (guint i = 0; i < g_strv_length(groups); i++) {
autocomplete_add(certs_ac, groups[i]);
}
@@ -90,33 +64,116 @@ tlscerts_init(void)
}
void
tlscerts_set_current(const char* const fp)
tlscerts_set_current(const TLSCertificate* cert)
{
if (current_fp) {
free(current_fp);
g_free(current_fp);
}
current_fp = strdup(fp);
current_fp = g_strdup(cert->fingerprint);
}
char*
tlscerts_get_current(void)
gboolean
tlscerts_current_fingerprint_equals(const TLSCertificate* cert)
{
return current_fp;
return g_strcmp0(current_fp, cert->fingerprint) == 0;
}
void
tlscerts_clear_current(void)
{
if (current_fp) {
free(current_fp);
g_free(current_fp);
current_fp = NULL;
}
}
gboolean
tlscerts_exists(const char* const fingerprint)
tlscerts_exists(const TLSCertificate* cert)
{
return g_key_file_has_group(tlscerts, fingerprint);
return g_key_file_has_group(tlscerts, cert->fingerprint);
}
#define _name_to_tlscert_name(in, cert, which) \
do { \
_name_to_tlscert_name_(in, &cert->which, #which); \
} while (0)
static void
_name_to_tlscert_name_(const char* in, tls_cert_name_t* out, const char* name);
static TLSCertificate*
_tlscerts_new(gchar* fingerprint_sha1, int version, gchar* serialnumber, gchar* subjectname,
gchar* issuername, gchar* notbefore, gchar* notafter,
gchar* key_alg, gchar* signature_alg, gchar* pem,
gchar* fingerprint_sha256, gchar* pubkey_fingerprint)
{
TLSCertificate* cert = g_new0(TLSCertificate, 1);
if (fingerprint_sha256 && fingerprint_sha1) {
cert->fingerprint_sha256 = fingerprint_sha256;
cert->fingerprint_sha1 = fingerprint_sha1;
cert->fingerprint = cert->fingerprint_sha256;
} else {
if (fingerprint_sha256) {
size_t s = strlen(fingerprint_sha256);
switch (s) {
case 40:
cert->fingerprint_sha1 = fingerprint_sha256;
cert->fingerprint = cert->fingerprint_sha1;
break;
case 64:
cert->fingerprint_sha256 = fingerprint_sha256;
cert->fingerprint = cert->fingerprint_sha256;
break;
default:
log_error("fingerprint_sha256 of length %zu unexpected: %s", s, fingerprint_sha256);
}
} else {
cert->fingerprint_sha1 = fingerprint_sha1;
cert->fingerprint = cert->fingerprint_sha1;
}
}
cert->version = version;
cert->serialnumber = serialnumber;
cert->subjectname = subjectname;
cert->issuername = issuername;
cert->notbefore = notbefore;
cert->notafter = notafter;
cert->key_alg = key_alg;
cert->signature_alg = signature_alg;
cert->pem = pem;
cert->pubkey_fingerprint = pubkey_fingerprint;
/* Do this check after assigning all values, in order to not leak them,
* even though the situation is most likely already FUBAR */
if (!fingerprint_sha256 && !fingerprint_sha1) {
log_error("No TLSCertificate w/o fingerprint");
tlscerts_free(cert);
return NULL;
}
_name_to_tlscert_name(subjectname, cert, subject);
_name_to_tlscert_name(issuername, cert, issuer);
return cert;
}
static TLSCertificate*
_get_TLSCertificate(const gchar* fingerprint)
{
int version = g_key_file_get_integer(tlscerts, fingerprint, "version", NULL);
gchar* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
gchar* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
gchar* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
gchar* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
gchar* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
gchar* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
gchar* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
gchar* fingerprint_sha1 = g_key_file_get_string(tlscerts, fingerprint, "fingerprint_sha1", NULL);
gchar* pubkey_fingerprint = g_key_file_get_string(tlscerts, fingerprint, "pubkey_fingerprint", NULL);
return _tlscerts_new(fingerprint_sha1, version, serialnumber, subjectname, issuername, notbefore,
notafter, keyalg, signaturealg, NULL, g_strdup(fingerprint), pubkey_fingerprint);
}
GList*
@@ -126,125 +183,81 @@ tlscerts_list(void)
gsize len = 0;
auto_gcharv gchar** groups = g_key_file_get_groups(tlscerts, &len);
for (int i = 0; i < g_strv_length(groups); i++) {
char* fingerprint = strdup(groups[i]);
int version = g_key_file_get_integer(tlscerts, fingerprint, "version", NULL);
auto_gchar gchar* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
auto_gchar gchar* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
auto_gchar gchar* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
auto_gchar gchar* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
auto_gchar gchar* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
auto_gchar gchar* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
auto_gchar gchar* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
notafter, keyalg, signaturealg, NULL);
res = g_list_append(res, cert);
for (gsize i = 0; i < len; i++) {
res = g_list_append(res, _get_TLSCertificate(groups[i]));
}
return res;
}
TLSCertificate*
tlscerts_new(const char* const fingerprint, int version, const char* const serialnumber, const char* const subjectname,
const char* const issuername, const char* const notbefore, const char* const notafter,
const char* const key_alg, const char* const signature_alg, const char* const pem)
static void
_name_to_tlscert_name_(const char* in, tls_cert_name_t* out, const char* name)
{
TLSCertificate* cert = calloc(1, sizeof(TLSCertificate));
if (fingerprint) {
cert->fingerprint = strdup(fingerprint);
}
cert->version = version;
if (serialnumber) {
cert->serialnumber = strdup(serialnumber);
}
if (subjectname) {
cert->subjectname = strdup(subjectname);
}
if (issuername) {
cert->issuername = strdup(issuername);
}
if (notbefore) {
cert->notbefore = strdup(notbefore);
}
if (notafter) {
cert->notafter = strdup(notafter);
}
if (key_alg) {
cert->key_alg = strdup(key_alg);
}
if (signature_alg) {
cert->signature_alg = strdup(signature_alg);
}
if (pem) {
cert->pem = strdup(pem);
}
auto_gcharv gchar** fields = g_strsplit(subjectname, "/", 0);
for (int i = 0; i < g_strv_length(fields); i++) {
auto_gcharv gchar** fields = g_strsplit(in, "/", 0);
const guint fields_num = g_strv_length(fields);
for (guint i = 0; i < fields_num; i++) {
auto_gcharv gchar** keyval = g_strsplit(fields[i], "=", 2);
if (g_strv_length(keyval) == 2) {
#define tls_cert_name_set(which) \
do { \
if (out->which) { \
log_warning("%s." #which " already set, skip %s", name, keyval[1]); \
continue; \
} \
out->which = g_strdup(keyval[1]); \
} while (0)
if ((g_strcmp0(keyval[0], "C") == 0) || (g_strcmp0(keyval[0], "countryName") == 0)) {
cert->subject_country = strdup(keyval[1]);
tls_cert_name_set(country);
}
if ((g_strcmp0(keyval[0], "ST") == 0) || (g_strcmp0(keyval[0], "stateOrProvinceName") == 0)) {
cert->subject_state = strdup(keyval[1]);
tls_cert_name_set(state);
}
if (g_strcmp0(keyval[0], "dnQualifier") == 0) {
cert->subject_distinguishedname = strdup(keyval[1]);
tls_cert_name_set(distinguishedname);
}
if (g_strcmp0(keyval[0], "serialnumber") == 0) {
cert->subject_serialnumber = strdup(keyval[1]);
tls_cert_name_set(serialnumber);
}
if ((g_strcmp0(keyval[0], "CN") == 0) || (g_strcmp0(keyval[0], "commonName") == 0)) {
cert->subject_commonname = strdup(keyval[1]);
tls_cert_name_set(commonname);
}
if ((g_strcmp0(keyval[0], "O") == 0) || (g_strcmp0(keyval[0], "organizationName") == 0)) {
cert->subject_organisation = strdup(keyval[1]);
tls_cert_name_set(organisation);
}
if ((g_strcmp0(keyval[0], "OU") == 0) || (g_strcmp0(keyval[0], "organizationalUnitName") == 0)) {
cert->subject_organisation_unit = strdup(keyval[1]);
tls_cert_name_set(organisation_unit);
}
if (g_strcmp0(keyval[0], "emailAddress") == 0) {
cert->subject_email = strdup(keyval[1]);
tls_cert_name_set(email);
}
#undef tls_cert_name_set
}
}
}
auto_gcharv gchar** fields2 = g_strsplit(issuername, "/", 0);
for (int i = 0; i < g_strv_length(fields2); i++) {
auto_gcharv gchar** keyval = g_strsplit(fields2[i], "=", 2);
if (g_strv_length(keyval) == 2) {
if ((g_strcmp0(keyval[0], "C") == 0) || (g_strcmp0(keyval[0], "countryName") == 0)) {
cert->issuer_country = strdup(keyval[1]);
}
if ((g_strcmp0(keyval[0], "ST") == 0) || (g_strcmp0(keyval[0], "stateOrProvinceName") == 0)) {
cert->issuer_state = strdup(keyval[1]);
}
if (g_strcmp0(keyval[0], "dnQualifier") == 0) {
cert->issuer_distinguishedname = strdup(keyval[1]);
}
if (g_strcmp0(keyval[0], "serialnumber") == 0) {
cert->issuer_serialnumber = strdup(keyval[1]);
}
if ((g_strcmp0(keyval[0], "CN") == 0) || (g_strcmp0(keyval[0], "commonName") == 0)) {
cert->issuer_commonname = strdup(keyval[1]);
}
if ((g_strcmp0(keyval[0], "O") == 0) || (g_strcmp0(keyval[0], "organizationName") == 0)) {
cert->issuer_organisation = strdup(keyval[1]);
}
if ((g_strcmp0(keyval[0], "OU") == 0) || (g_strcmp0(keyval[0], "organizationalUnitName") == 0)) {
cert->issuer_organisation_unit = strdup(keyval[1]);
}
if (g_strcmp0(keyval[0], "emailAddress") == 0) {
cert->issuer_email = strdup(keyval[1]);
}
}
}
TLSCertificate*
tlscerts_new(const char* fingerprint_sha1, int version, const char* serialnumber, const char* subjectname,
const char* issuername, const char* notbefore, const char* notafter,
const char* key_alg, const char* signature_alg, const char* pem,
const char* fingerprint_sha256, const char* pubkey_fingerprint)
{
return _tlscerts_new(g_strdup(fingerprint_sha1), version, g_strdup(serialnumber), g_strdup(subjectname),
g_strdup(issuername), g_strdup(notbefore), g_strdup(notafter),
g_strdup(key_alg), g_strdup(signature_alg), g_strdup(pem),
g_strdup(fingerprint_sha256), g_strdup(pubkey_fingerprint));
}
return cert;
static void
_checked_g_key_file_set_string(GKeyFile* key_file,
const gchar* group_name,
const gchar* key,
const gchar* string)
{
if (string == NULL)
return;
g_key_file_set_string(key_file, group_name, key, string);
}
void
@@ -261,33 +274,21 @@ tlscerts_add(const TLSCertificate* cert)
autocomplete_add(certs_ac, cert->fingerprint);
g_key_file_set_integer(tlscerts, cert->fingerprint, "version", cert->version);
if (cert->serialnumber) {
g_key_file_set_string(tlscerts, cert->fingerprint, "serialnumber", cert->serialnumber);
}
if (cert->subjectname) {
g_key_file_set_string(tlscerts, cert->fingerprint, "subjectname", cert->subjectname);
}
if (cert->issuername) {
g_key_file_set_string(tlscerts, cert->fingerprint, "issuername", cert->issuername);
}
if (cert->notbefore) {
g_key_file_set_string(tlscerts, cert->fingerprint, "start", cert->notbefore);
}
if (cert->notafter) {
g_key_file_set_string(tlscerts, cert->fingerprint, "end", cert->notafter);
}
if (cert->key_alg) {
g_key_file_set_string(tlscerts, cert->fingerprint, "keyalg", cert->key_alg);
}
if (cert->signature_alg) {
g_key_file_set_string(tlscerts, cert->fingerprint, "signaturealg", cert->signature_alg);
}
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "serialnumber", cert->serialnumber);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "subjectname", cert->subjectname);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "issuername", cert->issuername);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "start", cert->notbefore);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "end", cert->notafter);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "keyalg", cert->key_alg);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "signaturealg", cert->signature_alg);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "fingerprint_sha1", cert->fingerprint_sha1);
_checked_g_key_file_set_string(tlscerts, cert->fingerprint, "pubkey_fingerprint", cert->pubkey_fingerprint);
_save_tlscerts();
}
gboolean
tlscerts_revoke(const char* const fingerprint)
tlscerts_revoke(const char* fingerprint)
{
gboolean result = g_key_file_remove_group(tlscerts, fingerprint, NULL);
if (result) {
@@ -300,28 +301,24 @@ tlscerts_revoke(const char* const fingerprint)
}
TLSCertificate*
tlscerts_get_trusted(const char* const fingerprint)
tlscerts_get_trusted(const gchar* fingerprint)
{
if (!g_key_file_has_group(tlscerts, fingerprint)) {
gsize len = 0;
auto_gcharv gchar** groups = g_key_file_get_groups(tlscerts, &len);
for (gsize i = 0; i < len; i++) {
auto_gchar gchar* fingerprint_sha1 = g_key_file_get_string(tlscerts, groups[i], "fingerprint_sha1", NULL);
if (g_strcmp0(fingerprint, fingerprint_sha1) == 0) {
return _get_TLSCertificate(fingerprint_sha1);
}
}
return NULL;
}
int version = g_key_file_get_integer(tlscerts, fingerprint, "version", NULL);
auto_gchar gchar* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
auto_gchar gchar* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
auto_gchar gchar* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
auto_gchar gchar* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
auto_gchar gchar* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
auto_gchar gchar* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
auto_gchar gchar* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
notafter, keyalg, signaturealg, NULL);
return cert;
return _get_TLSCertificate(fingerprint);
}
char*
tlscerts_complete(const char* const prefix, gboolean previous, void* context)
tlscerts_complete(const char* prefix, gboolean previous, void* context)
{
return autocomplete_complete(certs_ac, prefix, TRUE, previous);
}
@@ -332,40 +329,37 @@ tlscerts_reset_ac(void)
autocomplete_reset(certs_ac);
}
static void
_tls_cert_name_free(tls_cert_name_t* name)
{
g_free(name->country);
g_free(name->state);
g_free(name->distinguishedname);
g_free(name->serialnumber);
g_free(name->commonname);
g_free(name->organisation);
g_free(name->organisation_unit);
g_free(name->email);
}
void
tlscerts_free(TLSCertificate* cert)
{
if (cert) {
free(cert->serialnumber);
_tls_cert_name_free(&cert->subject);
_tls_cert_name_free(&cert->issuer);
free(cert->subjectname);
free(cert->subject_country);
free(cert->subject_state);
free(cert->subject_distinguishedname);
free(cert->subject_serialnumber);
free(cert->subject_commonname);
free(cert->subject_organisation);
free(cert->subject_organisation_unit);
free(cert->subject_email);
free(cert->issuername);
free(cert->issuer_country);
free(cert->issuer_state);
free(cert->issuer_distinguishedname);
free(cert->issuer_serialnumber);
free(cert->issuer_commonname);
free(cert->issuer_organisation);
free(cert->issuer_organisation_unit);
free(cert->issuer_email);
free(cert->notbefore);
free(cert->notafter);
free(cert->fingerprint);
free(cert->key_alg);
free(cert->signature_alg);
free(cert->pem);
g_free(cert->pubkey_fingerprint);
g_free(cert->pem);
g_free(cert->signature_alg);
g_free(cert->key_alg);
g_free(cert->notafter);
g_free(cert->notbefore);
g_free(cert->issuername);
g_free(cert->subjectname);
g_free(cert->serialnumber);
g_free(cert->fingerprint_sha1);
g_free(cert->fingerprint_sha256);
free(cert);
}

View File

@@ -4,33 +4,7 @@
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_TLSCERTS_H
@@ -38,61 +12,63 @@
#include <glib.h>
typedef struct tls_cert_name_t
{
gchar* country;
gchar* state;
gchar* distinguishedname;
gchar* serialnumber;
gchar* commonname;
gchar* organisation;
gchar* organisation_unit;
gchar* email;
} tls_cert_name_t;
typedef struct tls_cert_t
{
int version;
char* serialnumber;
char* subjectname;
char* subject_country;
char* subject_state;
char* subject_distinguishedname;
char* subject_serialnumber;
char* subject_commonname;
char* subject_organisation;
char* subject_organisation_unit;
char* subject_email;
char* issuername;
char* issuer_country;
char* issuer_state;
char* issuer_distinguishedname;
char* issuer_serialnumber;
char* issuer_commonname;
char* issuer_organisation;
char* issuer_organisation_unit;
char* issuer_email;
char* notbefore;
char* notafter;
char* fingerprint;
char* key_alg;
char* signature_alg;
char* pem;
const gchar* fingerprint;
gchar* serialnumber;
gchar* subjectname;
tls_cert_name_t subject;
gchar* issuername;
tls_cert_name_t issuer;
gchar* notbefore;
gchar* notafter;
gchar* fingerprint_sha1;
gchar* fingerprint_sha256;
gchar* key_alg;
gchar* signature_alg;
gchar* pem;
gchar* pubkey_fingerprint;
} TLSCertificate;
void tlscerts_init(void);
TLSCertificate* tlscerts_new(const char* const fingerprint, int version, const char* const serialnumber, const char* const subjectname,
const char* const issuername, const char* const notbefore, const char* const notafter,
const char* const key_alg, const char* const signature_alg, const char* const pem);
TLSCertificate* tlscerts_new(const char* fingerprint_sha1, int version, const char* serialnumber, const char* subjectname,
const char* issuername, const char* notbefore, const char* notafter,
const char* key_alg, const char* signature_alg, const char* pem,
const char* fingerprint_sha256, const char* pubkey_fingerprint);
void tlscerts_set_current(const char* const fp);
void tlscerts_set_current(const TLSCertificate* cert);
char* tlscerts_get_current(void);
gboolean tlscerts_current_fingerprint_equals(const TLSCertificate* cert);
void tlscerts_clear_current(void);
gboolean tlscerts_exists(const char* const fingerprint);
gboolean tlscerts_exists(const TLSCertificate* cert);
void tlscerts_add(const TLSCertificate* cert);
gboolean tlscerts_revoke(const char* const fingerprint);
gboolean tlscerts_revoke(const char* fingerprint);
TLSCertificate* tlscerts_get_trusted(const char* const fingerprint);
TLSCertificate* tlscerts_get_trusted(const char* fingerprint);
void tlscerts_free(TLSCertificate* cert);
GList* tlscerts_list(void);
char* tlscerts_complete(const char* const prefix, gboolean previous, void* context);
char* tlscerts_complete(const char* prefix, gboolean previous, void* context);
void tlscerts_reset_ac(void);