Compare commits

...

5 Commits

Author SHA1 Message Date
b7dec705c3 fix(autocomplete): guard history completion safely
All checks were successful
CI Code / Check spelling (pull_request) Successful in 12s
CI Code / Check coding style (pull_request) Successful in 23s
CI Code / Code Coverage (pull_request) Successful in 3m40s
CI Code / Linux (debian) (pull_request) Successful in 5m14s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m20s
CI Code / Linux (arch) (pull_request) Successful in 7m23s
Prevent autocomplete crashes on asset failures when executing history subcommands without an active connection by validating the session state beforehand. Standardize JID resolution across command handlers to ensure consistent contact lookup behavior.

Use auto_gchar gchar instead of GString for performance and readability purposes.
2026-07-11 16:09:36 +00:00
b208e48ee1 feat(cli): resolve contact names to JIDs in multiple commands and improve autocomplete
Introduce _resolve_contact_jid helper to convert contact names to JIDs.
Apply this helper to /caps, /software, /disco, /lastactivity, and /ping
commands, allowing users to specify contacts by name instead of full JIDs.

Enhance command-line autocomplete by adding _disco_autocomplete and
_roster_jid_autocomplete functions. Update /caps autocomplete to utilize
the new roster resolution logic and ensure it only returns results when
connected.
2026-07-11 15:54:32 +00:00
c5dd0cf9df fix(xmpp): obfuscate client identity in protocol exchanges
All checks were successful
CI Code / Check spelling (pull_request) Successful in 12s
CI Code / Check coding style (pull_request) Successful in 24s
CI Code / Code Coverage (pull_request) Successful in 3m54s
CI Code / Linux (debian) (pull_request) Successful in 5m16s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m22s
CI Code / Linux (arch) (pull_request) Successful in 6m56s
CI Code / Check spelling (push) Successful in 13s
CI Code / Check coding style (push) Successful in 27s
CI Code / Code Coverage (push) Successful in 3m35s
CI Code / Linux (debian) (push) Successful in 5m41s
CI Code / Linux (ubuntu) (push) Successful in 5m52s
CI Code / Linux (arch) (push) Successful in 7m36s
Modify version responses to return a generic client name and omit
version strings. Drop the "profanity." prefix from dynamically
generated JID resources. Spoof the XEP-0115 capabilities node URI to
prevent service discovery/presence fingerprinting. These adjustments
reduce the client's attack surface by minimizing identifiable metadata.
2026-07-10 08:54:24 +00:00
b34faa2099 feat(caps): add cache clearing to /caps
Add /caps clear to wipe the local capabilities cache.
Implement autocomplete for new subcommand.
2026-07-10 08:12:56 +00:00
756d4e18cf fix(accounts): complete client.name to client.account_name rename
All checks were successful
CI Code / Check spelling (pull_request) Successful in 12s
CI Code / Check coding style (pull_request) Successful in 22s
CI Code / Code Coverage (pull_request) Successful in 3m21s
CI Code / Linux (debian) (pull_request) Successful in 5m12s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m18s
CI Code / Check spelling (push) Successful in 13s
CI Code / Check coding style (push) Successful in 23s
CI Code / Linux (ubuntu) (push) Successful in 4m43s
CI Code / Linux (debian) (push) Successful in 9m8s
CI Code / Linux (arch) (pull_request) Successful in 13m3s
CI Code / Code Coverage (push) Successful in 9m17s
CI Code / Linux (arch) (push) Successful in 13m4s
The previous rename of the account option key left "client.name" in
accounts_set_client() and accounts_clear_client(), preventing the
option from being set/cleared. Update both functions to use the
correct "client.account_name" key.

Note: existing configurations with the old key will not be migrated
automatically; a migration fix will follow.
2026-07-08 11:00:31 +00:00
21 changed files with 285 additions and 146 deletions

View File

@@ -42,6 +42,7 @@
#include "ai/ai_client.h"
static char* _caps_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _sub_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _notify_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _theme_autocomplete(ProfWin* window, const char* const input, gboolean previous);
@@ -109,6 +110,8 @@ static char* _software_autocomplete(ProfWin* window, const char* const input, gb
static char* _url_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _executable_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _lastactivity_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _disco_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _roster_jid_autocomplete(const char* const input, const char* const prefix, gboolean previous);
static char* _intype_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _mood_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _strophe_autocomplete(ProfWin* window, const char* const input, gboolean previous);
@@ -121,6 +124,7 @@ static char* _ai_autocomplete(ProfWin* window, const char* const input, gboolean
static char* _script_autocomplete_func(const char* const prefix, gboolean previous, void* context);
static char* _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previous);
static gboolean _is_connected(void);
static Autocomplete commands_ac;
static Autocomplete who_room_ac;
@@ -151,6 +155,7 @@ static Autocomplete account_clear_ac;
static Autocomplete account_default_ac;
static Autocomplete account_status_ac;
static Autocomplete disco_ac;
static Autocomplete caps_subcommands_ac;
static Autocomplete wins_ac;
static Autocomplete roster_ac;
static Autocomplete roster_show_ac;
@@ -321,6 +326,7 @@ static Autocomplete* all_acs[] = {
&account_default_ac,
&account_status_ac,
&disco_ac,
&caps_subcommands_ac,
&wins_ac,
&roster_ac,
&roster_show_ac,
@@ -597,6 +603,8 @@ cmd_ac_init(void)
autocomplete_add(disco_ac, "info");
autocomplete_add(disco_ac, "items");
autocomplete_add(caps_subcommands_ac, "clear");
autocomplete_add(account_ac, "list");
autocomplete_add(account_ac, "show");
autocomplete_add(account_ac, "add");
@@ -1414,12 +1422,14 @@ cmd_ac_init(void)
g_hash_table_insert(ac_funcs, "/ban", _ban_autocomplete);
g_hash_table_insert(ac_funcs, "/blocked", _blocked_autocomplete);
g_hash_table_insert(ac_funcs, "/bookmark", _bookmark_autocomplete);
g_hash_table_insert(ac_funcs, "/caps", _caps_autocomplete);
g_hash_table_insert(ac_funcs, "/clear", _clear_autocomplete);
g_hash_table_insert(ac_funcs, "/close", _close_autocomplete);
g_hash_table_insert(ac_funcs, "/cmd", _adhoc_cmd_autocomplete);
g_hash_table_insert(ac_funcs, "/color", _color_autocomplete);
g_hash_table_insert(ac_funcs, "/connect", _connect_autocomplete);
g_hash_table_insert(ac_funcs, "/console", _console_autocomplete);
g_hash_table_insert(ac_funcs, "/disco", _disco_autocomplete);
g_hash_table_insert(ac_funcs, "/correct", _correct_autocomplete);
g_hash_table_insert(ac_funcs, "/correction", _correction_autocomplete);
g_hash_table_insert(ac_funcs, "/executable", _executable_autocomplete);
@@ -1856,10 +1866,12 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
}
gchar* history_jid_subcmds[] = { "/history verify", "/history export", "/history import" };
for (size_t i = 0; i < ARRAY_SIZE(history_jid_subcmds); i++) {
result = autocomplete_param_with_func(input, history_jid_subcmds[i], roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
if (conn_status == JABBER_CONNECTED) {
for (size_t i = 0; i < ARRAY_SIZE(history_jid_subcmds); i++) {
result = autocomplete_param_with_func(input, history_jid_subcmds[i], roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
}
}
}
@@ -1935,7 +1947,6 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
Autocomplete completer;
} ac_cmds[] = {
{ "/prefs", prefs_ac },
{ "/disco", disco_ac },
{ "/room", room_ac },
{ "/mainwin", winpos_ac },
{ "/inputwin", winpos_ac },
@@ -1985,6 +1996,69 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
return NULL;
}
static gboolean
_is_connected(void)
{
return connection_get_status() == JABBER_CONNECTED;
}
static char*
_roster_jid_autocomplete(const char* const input, const char* const prefix, gboolean previous)
{
if (!_is_connected()) {
return NULL;
}
char* result = NULL;
result = autocomplete_param_with_func(input, prefix, roster_contact_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, prefix, roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, prefix, roster_fulljid_autocomplete, previous, NULL);
return result;
}
static char*
_caps_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/caps", caps_subcommands_ac, TRUE, previous);
if (result) {
return result;
}
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
if (nick_ac) {
result = autocomplete_param_with_ac(input, "/caps", nick_ac, TRUE, previous);
}
} else {
result = _roster_jid_autocomplete(input, "/caps", previous);
}
return result;
}
static char*
_disco_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/disco", disco_ac, TRUE, previous);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/disco info", previous);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/disco items", previous);
return result;
}
static char*
_sub_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
@@ -3663,7 +3737,7 @@ _win_autocomplete(ProfWin* window, const char* const input, gboolean previous)
}
char* unquoted = strip_arg_quotes(input);
result = autocomplete_param_with_func(unquoted, "/win", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/win", previous);
free(unquoted);
return result;
}
@@ -3997,18 +4071,14 @@ _invite_autocomplete(ProfWin* window, const char* const input, gboolean previous
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
result = _roster_jid_autocomplete(input, "/invite send", previous);
if (result) {
return result;
}
if (conn_status == JABBER_CONNECTED) {
result = autocomplete_param_with_func(input, "/invite send", roster_contact_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/invite decline", muc_invites_find, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/invite decline", muc_invites_find, previous, NULL);
if (result) {
return result;
}
return NULL;
@@ -4024,43 +4094,38 @@ _status_autocomplete(ProfWin* window, const char* const input, gboolean previous
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
// complete with: online, away etc.
result = autocomplete_param_with_ac(input, "/status set", account_status_ac, TRUE, previous);
if (result) {
return result;
}
if (conn_status == JABBER_CONNECTED) {
// Remove quote character before and after names when doing autocomplete
char* unquoted = strip_arg_quotes(input);
// complete with: online, away etc.
result = autocomplete_param_with_ac(input, "/status set", account_status_ac, TRUE, previous);
if (result) {
return result;
}
// Remove quote character before and after names when doing autocomplete
char* unquoted = strip_arg_quotes(input);
// MUC completion with nicknames
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
if (nick_ac) {
result = autocomplete_param_with_ac(unquoted, "/status get", nick_ac, TRUE, previous);
if (result) {
free(unquoted);
return result;
}
}
// roster completion
} else {
result = autocomplete_param_with_func(unquoted, "/status get", roster_contact_autocomplete, previous, NULL);
// MUC completion with nicknames
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
if (nick_ac) {
result = autocomplete_param_with_ac(unquoted, "/status get", nick_ac, TRUE, previous);
if (result) {
free(unquoted);
return result;
}
}
free(unquoted);
// roster completion
} else {
result = _roster_jid_autocomplete(input, "/status get", previous);
if (result) {
free(unquoted);
return result;
}
}
free(unquoted);
return NULL;
}
@@ -4167,14 +4232,12 @@ _software_autocomplete(ProfWin* window, const char* const input, gboolean previo
{
char* result = NULL;
if (window->type == WIN_CHAT) {
if (window->type == WIN_CHAT && _is_connected()) {
ProfChatWin* chatwin = (ProfChatWin*)window;
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
GString* search_str = g_string_new("/software ");
g_string_append(search_str, chatwin->barejid);
result = autocomplete_param_with_func(search_str->str, "/software", roster_fulljid_autocomplete, previous, NULL);
g_string_free(search_str, TRUE);
auto_gchar gchar* search_str = g_strdup_printf("/software %s", chatwin->barejid);
result = autocomplete_param_with_func(search_str, "/software", roster_fulljid_autocomplete, previous, NULL);
} else if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
@@ -4188,10 +4251,7 @@ _software_autocomplete(ProfWin* window, const char* const input, gboolean previo
}
}
} else {
result = autocomplete_param_with_func(input, "/software", roster_fulljid_autocomplete, previous, NULL);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/software", previous);
}
return result;
@@ -4290,16 +4350,11 @@ _lastactivity_autocomplete(ProfWin* window, const char* const input, gboolean pr
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status == JABBER_CONNECTED) {
result = autocomplete_param_with_func(input, "/lastactivity set", prefs_autocomplete_boolean_choice, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/lastactivity get", roster_barejid_autocomplete, previous, NULL);
result = autocomplete_param_with_func(input, "/lastactivity set", prefs_autocomplete_boolean_choice, previous, NULL);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/lastactivity get", previous);
return result;
}
@@ -4479,19 +4534,19 @@ _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous)
} else {
char* unquoted = strip_arg_quotes(input);
result = autocomplete_param_with_func(unquoted, "/vcard get", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard get", previous);
if (result) {
free(unquoted);
return result;
}
result = autocomplete_param_with_func(unquoted, "/vcard photo open", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard photo open", previous);
if (result) {
free(unquoted);
return result;
}
result = autocomplete_param_with_func(unquoted, "/vcard photo save", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard photo save", previous);
if (result) {
free(unquoted);
return result;

View File

@@ -427,17 +427,21 @@ static const struct cmd_t command_defs[] = {
CMD_TAG_GROUPCHAT)
CMD_SYN(
"/caps",
"/caps <fulljid>|<nick>")
"/caps <fulljid>|<nick>",
"/caps clear")
CMD_DESC(
"Find out a contacts, or room members client software capabilities. "
"If in private chat initiated from a chat room, no parameter is required.")
"If in private chat initiated from a chat room, no parameter is required. "
"Use clear to remove all cached capabilities.")
CMD_ARGS(
{ "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see capabilities." },
{ "<nick>", "If in a chat room, nickname for which you wish to see capabilities." })
{ "<nick>", "If in a chat room, nickname for which you wish to see capabilities." },
{ "clear", "Clear all cached capabilities." })
CMD_EXAMPLES(
"/caps ran@cold.sea.org/laptop",
"/caps ran@cold.sea.org/phone",
"/caps aegir")
"/caps aegir",
"/caps clear")
},
{ CMD_PREAMBLE("/software",

View File

@@ -70,6 +70,7 @@
#include "xmpp/stanza.h"
#include "xmpp/vcard_funcs.h"
#include "xmpp/xmpp.h"
#include "xmpp/capabilities.h"
#ifdef HAVE_LIBOTR
#include "otr/otr.h"
@@ -109,6 +110,15 @@ static gboolean _cmd_execute_default(ProfWin* window, const char* inp);
static gboolean _cmd_execute_alias(ProfWin* window, const char* const inp, gboolean* ran);
static gboolean
_download_install_plugin(ProfWin* window, gchar* url, gchar* path);
static const gchar*
_resolve_contact_jid(const char* const name)
{
char* barejid = roster_barejid_from_name(name);
if (barejid == NULL) {
barejid = (char*)name;
}
return barejid;
}
static void
_vcard_editor_finished_cb(gchar* message, void* user_data)
@@ -3387,6 +3397,12 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
if (g_strcmp0(args[0], "clear") == 0) {
caps_cache_clear();
cons_show("Capabilities cache cleared.");
return TRUE;
}
switch (window->type) {
case WIN_MUC:
if (args[0]) {
@@ -3405,8 +3421,9 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
break;
case WIN_CHAT:
case WIN_CONSOLE:
case WIN_XML:
if (args[0]) {
auto_jid Jid* jid = jid_create(args[0]);
auto_jid Jid* jid = jid_create(_resolve_contact_jid(args[0]));
if (jid == NULL || jid->fulljid == NULL) {
cons_show("You must provide a full jid to the /caps command.");
@@ -3448,7 +3465,7 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
}
static void
_send_software_version_iq_to_fulljid(char* request)
_send_software_version_iq_to_fulljid(const gchar* const request)
{
auto_jid Jid* jid = jid_create(request);
@@ -3489,7 +3506,7 @@ cmd_software(ProfWin* window, const char* const command, gchar** args)
break;
case WIN_CHAT:
if (args[0]) {
_send_software_version_iq_to_fulljid(args[0]);
_send_software_version_iq_to_fulljid(_resolve_contact_jid(args[0]));
break;
} else {
ProfChatWin* chatwin = (ProfChatWin*)window;
@@ -3513,7 +3530,7 @@ cmd_software(ProfWin* window, const char* const command, gchar** args)
}
case WIN_CONSOLE:
if (args[0]) {
_send_software_version_iq_to_fulljid(args[0]);
_send_software_version_iq_to_fulljid(_resolve_contact_jid(args[0]));
} else {
cons_show("You must provide a jid to the /software command.");
}
@@ -4823,7 +4840,7 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
auto_gchar gchar* jid = g_strdup_printf("%s", args[1] ?: connection_get_jid()->domainpart);
auto_gchar gchar* jid = g_strdup_printf("%s", args[1] ? _resolve_contact_jid(args[1]) : connection_get_jid()->domainpart);
if (g_strcmp0(args[0], "info") == 0) {
iq_disco_info_request(jid);
@@ -5045,7 +5062,7 @@ cmd_lastactivity(ProfWin* window, const char* const command, gchar** args)
if (args[1] == NULL) {
iq_last_activity_request(connection_get_jid()->domainpart);
} else {
iq_last_activity_request(args[1]);
iq_last_activity_request(_resolve_contact_jid(args[1]));
}
return TRUE;
}
@@ -6398,17 +6415,17 @@ cmd_ping(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
if (args[0] != NULL && caps_jid_has_feature(args[0], XMPP_FEATURE_PING) == FALSE) {
cons_show("%s does not support ping requests.", args[0]);
return TRUE;
}
iq_send_ping(args[0]);
if (args[0] == NULL) {
cons_show("Pinged server…");
if (args[0] != NULL) {
const gchar* ping_target = _resolve_contact_jid(args[0]);
if (caps_jid_has_feature(ping_target, XMPP_FEATURE_PING) == FALSE) {
cons_show("%s does not support ping requests.", ping_target);
return TRUE;
}
iq_send_ping(ping_target);
cons_show("Pinged %s…", ping_target);
} else {
cons_show("Pinged %s…", args[0]);
iq_send_ping(NULL);
cons_show("Pinged server…");
}
return TRUE;
}

View File

@@ -921,7 +921,7 @@ prof_date_time_format_iso8601(GDateTime* dt)
return ret;
}
/* build profanity version string.
/* build CProof version string.
* example: 0.13.1dev.master.69d8c1f9
*/
gchar*

View File

@@ -522,7 +522,7 @@ accounts_set_script_start(const char* const account_name, const char* const valu
void
accounts_set_client(const char* const account_name, const char* const value)
{
_accounts_set_string_option(account_name, "client.name", value);
_accounts_set_string_option(account_name, "client.account_name", value);
}
void
@@ -576,7 +576,7 @@ accounts_clear_script_start(const char* const account_name)
void
accounts_clear_client(const char* const account_name)
{
_accounts_clear_string_option(account_name, "client.name");
_accounts_clear_string_option(account_name, "client.account_name");
}
void

View File

@@ -2689,7 +2689,6 @@ _get_default_boolean(preference_t pref)
case PREF_STATUSBAR_SHOW_NUMBER:
case PREF_STATUSBAR_SHOW_READ:
case PREF_STATUSBAR_SHOW_DBBACKEND:
case PREF_REVEAL_OS:
case PREF_CORRECTION_ALLOW:
case PREF_RECEIPTS_SEND:
case PREF_CARBONS:
@@ -2704,6 +2703,7 @@ _get_default_boolean(preference_t pref)
case PREF_STROPHE_SM_RESEND:
case PREF_AUTOPING_WARNING:
return TRUE;
case PREF_REVEAL_OS:
case PREF_SPELLCHECK_ENABLE:
case PREF_PGP_PUBKEY_AUTOIMPORT:
case PREF_FORCE_ENCRYPTION:

View File

@@ -77,7 +77,7 @@ main(int argc, char** argv)
if (version == TRUE) {
auto_gchar gchar* prof_version = prof_get_version();
g_print("Profanity, version %s\n", prof_version);
g_print("CProof, version %s\n", prof_version);
// lets use fixed email instead of PACKAGE_BUGREPORT
g_print("Copyright (C) 2012 - 2019 James Booth <boothj5web@gmail.com>.\n");

View File

@@ -253,7 +253,7 @@ _init(char* log_level, char* config_file, char* log_file, char* theme_name)
log_stderr_init(PROF_LEVEL_ERROR);
auto_gchar gchar* prof_version = prof_get_version();
log_info("Starting Profanity (%s)…", prof_version);
log_info("Starting CProof (%s)…", prof_version);
chatlog_init();
accounts_load();

View File

@@ -2830,8 +2830,7 @@ cons_privacy_setting(void)
if (account->client) {
cons_show("Client name (/account set <account> clientid) : %s", account->client);
} else {
auto_gchar gchar* prof_version = prof_get_version();
cons_show("Client name (/account set <account> clientid) : Profanity %s", prof_version);
cons_show("Client name (/account set <account> clientid) : Pidgin");
}
if (account->max_sessions > 0) {
cons_show("Max sessions alarm (/account set <account> session_alarm) : %d", account->max_sessions);

View File

@@ -23,6 +23,7 @@
#include "plugins/plugins.h"
#include "config/files.h"
#include "config/preferences.h"
#include "xmpp/connection.h"
#include "xmpp/xmpp.h"
#include "xmpp/stanza.h"
#include "xmpp/form.h"
@@ -271,6 +272,19 @@ caps_map_jid_to_ver(const char* const jid, const char* const ver)
g_hash_table_insert(jid_to_ver, strdup(jid), strdup(ver));
}
void
caps_cache_clear(void)
{
g_hash_table_remove_all(jid_to_ver);
g_hash_table_remove_all(jid_to_caps);
if (caps_prof_keyfile.keyfile) {
g_key_file_free(caps_prof_keyfile.keyfile);
}
caps_prof_keyfile.keyfile = g_key_file_new();
cache = caps_prof_keyfile.keyfile;
save_keyfile(&caps_prof_keyfile);
}
gboolean
caps_cache_contains(const char* const ver)
{

View File

@@ -28,5 +28,6 @@ void caps_map_jid_to_ver(const char* const jid, const char* const ver);
gboolean caps_cache_contains(const char* const ver);
GList* caps_get_features(void);
char* caps_get_my_sha1(xmpp_ctx_t* const ctx);
void caps_cache_clear(void);
#endif

View File

@@ -1681,7 +1681,7 @@ _version_get_handler(xmpp_stanza_t* const stanza)
xmpp_stanza_t* name = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(name, "name");
xmpp_stanza_t* name_txt = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(name_txt, is_custom_client ? client : "Profanity");
xmpp_stanza_set_text(name_txt, is_custom_client ? client : "Pidgin");
xmpp_stanza_add_child(name, name_txt);
xmpp_stanza_add_child(query, name);
bool include_os = prefs_get_boolean(PREF_REVEAL_OS) && !is_custom_client;
@@ -1690,7 +1690,7 @@ _version_get_handler(xmpp_stanza_t* const stanza)
xmpp_stanza_t* version = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(version, "version");
xmpp_stanza_t* version_txt = xmpp_stanza_new(ctx);
auto_gchar gchar* prof_version = prof_get_version();
auto_gchar gchar* prof_version = g_strdup("");
if (!is_custom_client) {
xmpp_stanza_set_text(version_txt, prof_version);

View File

@@ -280,9 +280,13 @@ jid_fulljid_or_barejid(Jid* jid)
gchar*
jid_random_resource(void)
{
auto_gchar gchar* rand = get_random_string(4);
gchar* rand = g_malloc0(29);
const gchar* digits = "0123456789";
gchar* result = g_strdup_printf("profanity.%s", rand);
for (int i = 0; i < 28; i++) {
rand[i] = digits[g_random_int_range(0, 10)];
}
rand[28] = '\0';
return result;
return rand;
}

View File

@@ -178,7 +178,7 @@ session_connect_with_details(const char* const jid, const char* const passwd, co
saved_details.auth_policy = NULL;
}
// use 'profanity' when no resourcepart in provided jid
// use random string when no resourcepart in provided jid
auto_jid Jid* jidp = jid_create(jid);
if (jidp->resourcepart == NULL) {
auto_gchar gchar* resource = jid_random_resource();

View File

@@ -32,6 +32,7 @@
static void _stanza_add_unique_id(xmpp_stanza_t* stanza);
static gchar* _stanza_create_sha1_hash(char* str);
static const char* _stanza_get_caps_node(const char* const client);
#if 0
xmpp_stanza_t*
@@ -930,23 +931,22 @@ stanza_create_caps_query_element(xmpp_ctx_t* ctx)
xmpp_stanza_t* identity = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(identity, "identity");
xmpp_stanza_set_type(identity, "pc");
xmpp_stanza_set_attribute(identity, "category", "client");
ProfAccount* account = accounts_get_account(session_get_account_name());
gchar* client = account->client;
bool is_custom_client = client != NULL;
GString* name_str = g_string_new(is_custom_client ? client : "Profanity ");
if (!is_custom_client) {
xmpp_stanza_set_type(identity, "console");
auto_gchar gchar* prof_version = prof_get_version();
g_string_append(name_str, prof_version);
auto_gchar gchar* name = g_strdup(account->client ? account->client : "Pidgin");
if (account->client) {
gchar* space = strchr(name, ' ');
if (space) {
*space = '\0';
}
}
account_free(account);
xmpp_stanza_set_attribute(identity, "name", name_str->str);
g_string_free(name_str, TRUE);
xmpp_stanza_set_attribute(identity, "name", name);
xmpp_stanza_add_child(query, identity);
xmpp_stanza_release(identity);
@@ -1994,6 +1994,42 @@ stanza_attach_last_activity(xmpp_ctx_t* const ctx,
xmpp_stanza_release(query);
}
static const char*
_stanza_get_caps_node(const char* const client)
{
if (!client || g_strcmp0(client, "") == 0) {
return "http://pidgin.im";
}
auto_gchar gchar* lower = g_ascii_strdown(client, -1);
if (g_str_has_prefix(lower, "pidgin")) {
return "http://pidgin.im";
}
if (g_str_has_prefix(lower, "gajim")) {
return "https://gajim.org";
}
if (g_str_has_prefix(lower, "conversations")) {
return "https://conversations.im";
}
if (g_str_has_prefix(lower, "psi+")) {
return "http://psi-plus.com";
}
if (g_str_has_prefix(lower, "psi")) {
return "http://psi-im.org";
}
if (g_str_has_prefix(lower, "profanity")) {
return "http://profanity-im.github.io";
}
return "";
}
void
stanza_attach_caps(xmpp_ctx_t* const ctx, xmpp_stanza_t* const presence)
{
@@ -2002,9 +2038,13 @@ stanza_attach_caps(xmpp_ctx_t* const ctx, xmpp_stanza_t* const presence)
xmpp_stanza_set_ns(caps, STANZA_NS_CAPS);
xmpp_stanza_t* query = stanza_create_caps_query_element(ctx);
ProfAccount* account = accounts_get_account(session_get_account_name());
const char* node = _stanza_get_caps_node(account->client);
account_free(account);
char* sha1 = caps_get_my_sha1(ctx);
xmpp_stanza_set_attribute(caps, STANZA_ATTR_HASH, "sha-1");
xmpp_stanza_set_attribute(caps, STANZA_ATTR_NODE, "http://profanity-im.github.io");
xmpp_stanza_set_attribute(caps, STANZA_ATTR_NODE, node);
xmpp_stanza_set_attribute(caps, STANZA_ATTR_VER, sha1);
xmpp_stanza_add_child(presence, caps);
xmpp_stanza_release(caps);

View File

@@ -601,11 +601,11 @@ prof_connect_with_roster(const char* roster)
assert_true(prof_output_regex(".+online.+ \\(priority 0\\)\\."));
expect_timeout = EXPECT_TIMEOUT_CONNECT;
// Wait for presence stanza to be sent (content-based, not ID-based)
// Wait for presence stanza to be sent
// Match the actual attribute order from stanza_attach_caps
assert_true(stbbr_received(
"<presence id=\"*\">"
"<c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://profanity-im.github.io\" ver=\"*\"/>"
"<c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"*\" ver=\"*\"/>"
"</presence>"));
}

View File

@@ -25,7 +25,7 @@ connect_jid_sends_presence_after_receiving_roster(void **state)
assert_true(stbbr_received(
"<presence id='*'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}

View File

@@ -1289,7 +1289,7 @@ _join_muc(const char* room)
snprintf(presence, sizeof(presence),
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='%s/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' "
"node='http://profanity-im.github.io' ver='*'/>"
"node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"

View File

@@ -26,7 +26,7 @@ sends_room_join(void **state)
assert_true(stbbr_received(
"<presence id='*' to='testroom@conference.localhost/stabber'>"
"<x xmlns='http://jabber.org/protocol/muc'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -41,7 +41,7 @@ sends_room_join_with_nick(void **state)
assert_true(stbbr_received(
"<presence id='*' to='testroom@conference.localhost/testnick'>"
"<x xmlns='http://jabber.org/protocol/muc'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -58,7 +58,7 @@ sends_room_join_with_password(void **state)
"<x xmlns='http://jabber.org/protocol/muc'>"
"<password>testpassword</password>"
"</x>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -75,7 +75,7 @@ sends_room_join_with_nick_and_password(void **state)
"<x xmlns='http://jabber.org/protocol/muc'>"
"<password>testpassword</password>"
"</x>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -87,7 +87,7 @@ shows_role_and_affiliation_on_join(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -107,7 +107,7 @@ shows_subject_on_join(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -135,7 +135,7 @@ shows_history_message(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -170,7 +170,7 @@ shows_occupant_join(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -200,7 +200,7 @@ shows_message(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -227,7 +227,7 @@ shows_me_message_from_occupant(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -254,7 +254,7 @@ shows_me_message_from_self(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -281,7 +281,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -322,7 +322,7 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -369,7 +369,7 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
"</x>"
@@ -401,7 +401,7 @@ sends_affiliation_list_request(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='moderator' jid='stabber@localhost/profanity' affiliation='owner'/>"
"</x>"
@@ -434,7 +434,7 @@ sends_kick_request(void **state)
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='*' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='moderator' jid='stabber@localhost/profanity' affiliation='admin'/>"
"</x>"

View File

@@ -19,7 +19,7 @@ presence_online(void **state)
assert_true(stbbr_received(
"<presence id='*'>"
"<status>online</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -36,7 +36,7 @@ presence_online_with_message(void **state)
assert_true(stbbr_received(
"<presence id='*'>"
"<status>Hi there</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -54,7 +54,7 @@ presence_away(void **state)
"<presence id='*'>"
"<show>away</show>"
"<status>away</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -72,7 +72,7 @@ presence_away_with_message(void **state)
"<presence id='*'>"
"<show>away</show>"
"<status>I'm not here for a bit</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -90,7 +90,7 @@ presence_xa(void **state)
"<presence id='*'>"
"<show>xa</show>"
"<status>xa</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -108,7 +108,7 @@ presence_xa_with_message(void **state)
"<presence id='*'>"
"<show>xa</show>"
"<status>Gone to the shops</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -126,7 +126,7 @@ presence_dnd(void **state)
"<presence id='*'>"
"<show>dnd</show>"
"<status>dnd</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -144,7 +144,7 @@ presence_dnd_with_message(void **state)
"<presence id='*'>"
"<show>dnd</show>"
"<status>Working</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -162,7 +162,7 @@ presence_chat(void **state)
"<presence id='*'>"
"<show>chat</show>"
"<status>chat</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -180,7 +180,7 @@ presence_chat_with_message(void **state)
"<presence id='*'>"
"<show>chat</show>"
"<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -197,7 +197,7 @@ presence_set_priority(void **state)
assert_true(stbbr_received(
"<presence id='*'>"
"<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -212,7 +212,7 @@ presence_includes_priority(void **state)
assert_true(stbbr_received(
"<presence id='*'>"
"<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
@@ -223,7 +223,7 @@ presence_includes_priority(void **state)
"<priority>25</priority>"
"<show>chat</show>"
"<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}
@@ -239,7 +239,7 @@ presence_keeps_status(void **state)
"<presence id='*'>"
"<show>chat</show>"
"<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
@@ -250,7 +250,7 @@ presence_keeps_status(void **state)
"<show>chat</show>"
"<status>Free to talk</status>"
"<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='*'/>"
"</presence>"
));
}

View File

@@ -500,6 +500,11 @@ caps_jid_has_feature(const char* const jid, const char* const feature)
return FALSE;
}
void
caps_cache_clear(void)
{
}
gboolean
bookmark_add(const char* jid, const char* nick, const char* password, const char* autojoin_str, const char* name)
{