1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-07-14 18:14:15 -04:00
profanity/src/command/cmd_funcs.c
Steffen Jaeckel 9cf78e59d5 auto-format
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-01 15:01:28 +01:00

9667 lines
302 KiB
C

/*
* cmd_funcs.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 Michael Vetter <jubalh@iodoru.org>
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
*
* 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.
*
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <langinfo.h>
#include <ctype.h>
// fork / execl
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include "profanity.h"
#include "log.h"
#include "common.h"
#include "command/cmd_funcs.h"
#include "command/cmd_defs.h"
#include "command/cmd_ac.h"
#include "config/files.h"
#include "config/accounts.h"
#include "config/account.h"
#include "config/preferences.h"
#include "config/theme.h"
#include "config/tlscerts.h"
#include "config/scripts.h"
#include "event/client_events.h"
#include "tools/http_upload.h"
#include "tools/http_download.h"
#include "tools/autocomplete.h"
#include "tools/parser.h"
#include "tools/bookmark_ignore.h"
#include "plugins/plugins.h"
#include "ui/ui.h"
#include "ui/window_list.h"
#include "xmpp/xmpp.h"
#include "xmpp/connection.h"
#include "xmpp/contact.h"
#include "xmpp/roster_list.h"
#include "xmpp/jid.h"
#include "xmpp/muc.h"
#include "xmpp/chat_session.h"
#include "xmpp/avatar.h"
#ifdef HAVE_LIBOTR
#include "otr/otr.h"
#endif
#ifdef HAVE_LIBGPGME
#include "pgp/gpg.h"
#include "xmpp/ox.h"
#endif
#ifdef HAVE_OMEMO
#include "omemo/omemo.h"
#include "xmpp/omemo.h"
#include "tools/aesgcm_download.h"
#endif
#ifdef HAVE_GTK
#include "ui/tray.h"
#include "tools/clipboard.h"
#endif
#ifdef HAVE_PYTHON
#include "plugins/python_plugins.h"
#endif
static void _update_presence(const resource_presence_t presence,
const char* const show, gchar** args);
static void _cmd_set_boolean_preference(gchar* arg, const char* const command,
const char* const display, preference_t pref);
static void _who_room(ProfWin* window, const char* const command, gchar** args);
static void _who_roster(ProfWin* window, const char* const command, gchar** args);
static gboolean _cmd_execute(ProfWin* window, const char* const command, const char* const inp);
static gboolean _cmd_execute_default(ProfWin* window, const char* inp);
static gboolean _cmd_execute_alias(ProfWin* window, const char* const inp, gboolean* ran);
/*
* Take a line of input and process it, return TRUE if profanity is to
* continue, FALSE otherwise
*/
gboolean
cmd_process_input(ProfWin* window, char* inp)
{
log_debug("Input received: %s", inp);
gboolean result = FALSE;
g_strchomp(inp);
// just carry on if no input
if (strlen(inp) == 0) {
result = TRUE;
// handle command if input starts with a '/'
} else if (inp[0] == '/') {
char* inp_cpy = strdup(inp);
char* command = strtok(inp_cpy, " ");
char* question_mark = strchr(command, '?');
if (question_mark) {
*question_mark = '\0';
gchar* fakeinp = g_strdup_printf("/help %s", command + 1);
if (fakeinp) {
result = _cmd_execute(window, "/help", fakeinp);
g_free(fakeinp);
}
} else {
result = _cmd_execute(window, command, inp);
}
free(inp_cpy);
// call a default handler if input didn't start with '/'
} else {
result = _cmd_execute_default(window, inp);
}
return result;
}
// Command execution
void
cmd_execute_connect(ProfWin* window, const char* const account)
{
GString* command = g_string_new("/connect ");
g_string_append(command, account);
cmd_process_input(window, command->str);
g_string_free(command, TRUE);
}
gboolean
cmd_tls_certpath(ProfWin* window, const char* const command, gchar** args)
{
if (g_strcmp0(args[1], "set") == 0) {
if (args[2] == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (g_file_test(args[2], G_FILE_TEST_IS_DIR)) {
prefs_set_string(PREF_TLS_CERTPATH, args[2]);
cons_show("Certificate path set to: %s", args[2]);
} else {
cons_show("Directory %s does not exist.", args[2]);
}
return TRUE;
} else if (g_strcmp0(args[1], "clear") == 0) {
prefs_set_string(PREF_TLS_CERTPATH, "none");
cons_show("Certificate path cleared");
return TRUE;
} else if (g_strcmp0(args[1], "default") == 0) {
prefs_set_string(PREF_TLS_CERTPATH, NULL);
cons_show("Certificate path defaulted to finding system certpath.");
return TRUE;
} else if (args[1] == NULL) {
char* path = prefs_get_tls_certpath();
if (path) {
cons_show("Trusted certificate path: %s", path);
free(path);
} else {
cons_show("No trusted certificate path set.");
}
return TRUE;
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
}
gboolean
cmd_tls_trust(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are currently not connected.");
return TRUE;
}
if (!connection_is_secured()) {
cons_show("No TLS connection established");
return TRUE;
}
TLSCertificate* cert = connection_get_tls_peer_cert();
if (!cert) {
cons_show("Error getting TLS certificate.");
return TRUE;
}
if (tlscerts_exists(cert->fingerprint)) {
cons_show("Certificate %s already trusted.", cert->fingerprint);
tlscerts_free(cert);
return TRUE;
}
cons_show("Adding %s to trusted certificates.", cert->fingerprint);
tlscerts_add(cert);
tlscerts_free(cert);
return TRUE;
}
gboolean
cmd_tls_trusted(ProfWin* window, const char* const command, gchar** args)
{
GList* certs = tlscerts_list();
GList* curr = certs;
if (curr) {
cons_show("Trusted certificates:");
cons_show("");
} else {
cons_show("No trusted certificates found.");
}
while (curr) {
TLSCertificate* cert = curr->data;
cons_show_tlscert_summary(cert);
cons_show("");
curr = g_list_next(curr);
}
g_list_free_full(certs, (GDestroyNotify)tlscerts_free);
return TRUE;
}
gboolean
cmd_tls_revoke(ProfWin* window, const char* const command, gchar** args)
{
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
} else {
gboolean res = tlscerts_revoke(args[1]);
if (res) {
cons_show("Trusted certificate revoked: %s", args[1]);
} else {
cons_show("Could not find certificate: %s", args[1]);
}
}
return TRUE;
}
gboolean
cmd_tls_cert(ProfWin* window, const char* const command, gchar** args)
{
if (args[1]) {
TLSCertificate* cert = tlscerts_get_trusted(args[1]);
if (!cert) {
cons_show("No such certificate.");
} else {
cons_show_tlscert(cert);
tlscerts_free(cert);
}
return TRUE;
} else {
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
if (!connection_is_secured()) {
cons_show("No TLS connection established");
return TRUE;
}
TLSCertificate* cert = connection_get_tls_peer_cert();
if (!cert) {
cons_show("Error getting TLS certificate.");
return TRUE;
}
cons_show_tlscert(cert);
cons_show("");
tlscerts_free(cert);
return TRUE;
}
}
gboolean
cmd_connect(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_DISCONNECTED) {
cons_show("You are either connected already, or a login is in process.");
return TRUE;
}
gchar* opt_keys[] = { "server", "port", "tls", "auth", NULL };
gboolean parsed;
GHashTable* options = parse_options(&args[args[0] ? 1 : 0], opt_keys, &parsed);
if (!parsed) {
cons_bad_cmd_usage(command);
cons_show("");
options_destroy(options);
return TRUE;
}
char* altdomain = g_hash_table_lookup(options, "server");
char* tls_policy = g_hash_table_lookup(options, "tls");
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)) {
cons_bad_cmd_usage(command);
cons_show("");
options_destroy(options);
return TRUE;
}
char* auth_policy = g_hash_table_lookup(options, "auth");
if (auth_policy && (g_strcmp0(auth_policy, "default") != 0) && (g_strcmp0(auth_policy, "legacy") != 0)) {
cons_bad_cmd_usage(command);
cons_show("");
options_destroy(options);
return TRUE;
}
int port = 0;
if (g_hash_table_contains(options, "port")) {
char* port_str = g_hash_table_lookup(options, "port");
char* err_msg = NULL;
gboolean res = strtoi_range(port_str, &port, 1, 65535, &err_msg);
if (!res) {
cons_show(err_msg);
cons_show("");
free(err_msg);
port = 0;
options_destroy(options);
return TRUE;
}
}
char* user = args[0];
char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
if (!user) {
if (def) {
user = def;
cons_show("Using default account %s.", user);
} else {
cons_show("No default account.");
options_destroy(options);
return TRUE;
}
}
char* jid;
user = strdup(user);
g_free(def);
// connect with account
ProfAccount* account = accounts_get_account(user);
if (account) {
// override account options with connect options
if (altdomain != NULL)
account_set_server(account, altdomain);
if (port != 0)
account_set_port(account, port);
if (tls_policy != NULL)
account_set_tls_policy(account, tls_policy);
if (auth_policy != NULL)
account_set_auth_policy(account, auth_policy);
// use password if set
if (account->password) {
conn_status = cl_ev_connect_account(account);
// use eval_password if set
} else if (account->eval_password) {
gboolean res = account_eval_password(account);
if (res) {
conn_status = cl_ev_connect_account(account);
free(account->password);
account->password = NULL;
} else {
cons_show("Error evaluating password, see logs for details.");
account_free(account);
free(user);
options_destroy(options);
return TRUE;
}
// no account password setting, prompt
} else {
account->password = ui_ask_password(false);
conn_status = cl_ev_connect_account(account);
free(account->password);
account->password = NULL;
}
jid = account_create_connect_jid(account);
account_free(account);
// connect with JID
} else {
jid = g_utf8_strdown(user, -1);
char* passwd = ui_ask_password(false);
conn_status = cl_ev_connect_jid(jid, passwd, altdomain, port, tls_policy, auth_policy);
free(passwd);
}
if (conn_status == JABBER_DISCONNECTED) {
cons_show_error("Connection attempt for %s failed.", jid);
log_info("Connection attempt for %s failed", jid);
}
options_destroy(options);
free(jid);
free(user);
return TRUE;
}
gboolean
cmd_account_list(ProfWin* window, const char* const command, gchar** args)
{
gchar** accounts = accounts_get_list();
cons_show_account_list(accounts);
g_strfreev(accounts);
return TRUE;
}
gboolean
cmd_account_show(ProfWin* window, const char* const command, gchar** args)
{
char* account_name = args[1];
if (account_name == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
ProfAccount* account = accounts_get_account(account_name);
if (account == NULL) {
cons_show("No such account.");
cons_show("");
} else {
cons_show_account(account);
account_free(account);
}
return TRUE;
}
gboolean
cmd_account_add(ProfWin* window, const char* const command, gchar** args)
{
char* account_name = args[1];
if (account_name == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
accounts_add(account_name, NULL, 0, NULL, NULL);
cons_show("Account created.");
cons_show("");
return TRUE;
}
gboolean
cmd_account_remove(ProfWin* window, const char* const command, gchar** args)
{
char* account_name = args[1];
if (!account_name) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
if (accounts_remove(account_name)) {
cons_show("Account %s removed.", account_name);
if (def && strcmp(def, account_name) == 0) {
prefs_set_string(PREF_DEFAULT_ACCOUNT, NULL);
cons_show("Default account removed because the corresponding account was removed.");
}
} else {
cons_show("Failed to remove account %s.", account_name);
cons_show("Either the account does not exist, or an unknown error occurred.");
}
cons_show("");
g_free(def);
return TRUE;
}
gboolean
cmd_account_enable(ProfWin* window, const char* const command, gchar** args)
{
char* account_name = args[1];
if (account_name == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (accounts_enable(account_name)) {
cons_show("Account enabled.");
cons_show("");
} else {
cons_show("No such account: %s", account_name);
cons_show("");
}
return TRUE;
}
gboolean
cmd_account_disable(ProfWin* window, const char* const command, gchar** args)
{
char* account_name = args[1];
if (account_name == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (accounts_disable(account_name)) {
cons_show("Account disabled.");
cons_show("");
} else {
cons_show("No such account: %s", account_name);
cons_show("");
}
return TRUE;
}
gboolean
cmd_account_rename(ProfWin* window, const char* const command, gchar** args)
{
if (g_strv_length(args) != 3) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* account_name = args[1];
char* new_name = args[2];
if (accounts_rename(account_name, new_name)) {
cons_show("Account renamed.");
cons_show("");
} else {
cons_show("Either account %s doesn't exist, or account %s already exists.", account_name, new_name);
cons_show("");
}
return TRUE;
}
gboolean
cmd_account_default(ProfWin* window, const char* const command, gchar** args)
{
if (g_strv_length(args) == 1) {
char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
if (def) {
cons_show("The default account is %s.", def);
free(def);
} else {
cons_show("No default account.");
}
} else if (g_strv_length(args) == 2) {
if (strcmp(args[1], "off") == 0) {
prefs_set_string(PREF_DEFAULT_ACCOUNT, NULL);
cons_show("Removed default account.");
} else {
cons_bad_cmd_usage(command);
}
} else if (g_strv_length(args) == 3) {
if (strcmp(args[1], "set") == 0) {
ProfAccount* account_p = accounts_get_account(args[2]);
if (account_p) {
prefs_set_string(PREF_DEFAULT_ACCOUNT, args[2]);
cons_show("Default account set to %s.", args[2]);
account_free(account_p);
} else {
cons_show("Account %s does not exist.", args[2]);
}
} else {
cons_bad_cmd_usage(command);
}
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
}
gboolean
_account_set_jid(char* account_name, char* jid)
{
Jid* jidp = jid_create(jid);
if (jidp == NULL) {
cons_show("Malformed jid: %s", jid);
} else {
accounts_set_jid(account_name, jidp->barejid);
cons_show("Updated jid for account %s: %s", account_name, jidp->barejid);
if (jidp->resourcepart) {
accounts_set_resource(account_name, jidp->resourcepart);
cons_show("Updated resource for account %s: %s", account_name, jidp->resourcepart);
}
cons_show("");
}
jid_destroy(jidp);
return TRUE;
}
gboolean
_account_set_server(char* account_name, char* server)
{
accounts_set_server(account_name, server);
cons_show("Updated server for account %s: %s", account_name, server);
cons_show("");
return TRUE;
}
gboolean
_account_set_port(char* account_name, char* port)
{
int porti;
char* err_msg = NULL;
gboolean res = strtoi_range(port, &porti, 1, 65535, &err_msg);
if (!res) {
cons_show(err_msg);
cons_show("");
free(err_msg);
} else {
accounts_set_port(account_name, porti);
cons_show("Updated port for account %s: %s", account_name, port);
cons_show("");
}
return TRUE;
}
gboolean
_account_set_resource(char* account_name, char* resource)
{
accounts_set_resource(account_name, resource);
if (connection_get_status() == JABBER_CONNECTED) {
cons_show("Updated resource for account %s: %s, reconnect to pick up the change.", account_name, resource);
} else {
cons_show("Updated resource for account %s: %s", account_name, resource);
}
cons_show("");
return TRUE;
}
gboolean
_account_set_password(char* account_name, char* password)
{
ProfAccount* account = accounts_get_account(account_name);
if (account->eval_password) {
cons_show("Cannot set password when eval_password is set.");
} else {
accounts_set_password(account_name, password);
cons_show("Updated password for account %s", account_name);
cons_show("");
}
account_free(account);
return TRUE;
}
gboolean
_account_set_eval_password(char* account_name, char* eval_password)
{
ProfAccount* account = accounts_get_account(account_name);
if (account->password) {
cons_show("Cannot set eval_password when password is set.");
} else {
accounts_set_eval_password(account_name, eval_password);
cons_show("Updated eval_password for account %s", account_name);
cons_show("");
}
account_free(account);
return TRUE;
}
gboolean
_account_set_muc(char* account_name, char* muc)
{
accounts_set_muc_service(account_name, muc);
cons_show("Updated muc service for account %s: %s", account_name, muc);
cons_show("");
return TRUE;
}
gboolean
_account_set_nick(char* account_name, char* nick)
{
accounts_set_muc_nick(account_name, nick);
cons_show("Updated muc nick for account %s: %s", account_name, nick);
cons_show("");
return TRUE;
}
gboolean
_account_set_otr(char* account_name, char* policy)
{
if ((g_strcmp0(policy, "manual") != 0)
&& (g_strcmp0(policy, "opportunistic") != 0)
&& (g_strcmp0(policy, "always") != 0)) {
cons_show("OTR policy must be one of: manual, opportunistic or always.");
} else {
accounts_set_otr_policy(account_name, policy);
cons_show("Updated OTR policy for account %s: %s", account_name, policy);
cons_show("");
}
return TRUE;
}
gboolean
_account_set_status(char* account_name, char* status)
{
if (!valid_resource_presence_string(status) && (strcmp(status, "last") != 0)) {
cons_show("Invalid status: %s", status);
} else {
accounts_set_login_presence(account_name, status);
cons_show("Updated login status for account %s: %s", account_name, status);
}
cons_show("");
return TRUE;
}
gboolean
_account_set_pgpkeyid(char* account_name, char* pgpkeyid)
{
#ifdef HAVE_LIBGPGME
char* err_str = NULL;
if (!p_gpg_valid_key(pgpkeyid, &err_str)) {
cons_show("Invalid PGP key ID specified: %s, see /pgp keys", err_str);
} else {
accounts_set_pgp_keyid(account_name, pgpkeyid);
cons_show("Updated PGP key ID for account %s: %s", account_name, pgpkeyid);
}
free(err_str);
#else
cons_show("PGP support is not included in this build.");
#endif
cons_show("");
return TRUE;
}
gboolean
_account_set_startscript(char* account_name, char* script)
{
accounts_set_script_start(account_name, script);
cons_show("Updated start script for account %s: %s", account_name, script);
return TRUE;
}
gboolean
_account_set_theme(char* account_name, char* theme)
{
if (!theme_exists(theme)) {
cons_show("Theme does not exist: %s", theme);
return TRUE;
}
accounts_set_theme(account_name, theme);
if (connection_get_status() == JABBER_CONNECTED) {
ProfAccount* account = accounts_get_account(session_get_account_name());
if (account) {
if (g_strcmp0(account->name, account_name) == 0) {
theme_load(theme, false);
ui_load_colours();
if (prefs_get_boolean(PREF_ROSTER)) {
ui_show_roster();
} else {
ui_hide_roster();
}
if (prefs_get_boolean(PREF_OCCUPANTS)) {
ui_show_all_room_rosters();
} else {
ui_hide_all_room_rosters();
}
ui_redraw();
}
account_free(account);
}
}
cons_show("Updated theme for account %s: %s", account_name, theme);
return TRUE;
}
gboolean
_account_set_tls(char* account_name, char* policy)
{
if ((g_strcmp0(policy, "force") != 0)
&& (g_strcmp0(policy, "allow") != 0)
&& (g_strcmp0(policy, "trust") != 0)
&& (g_strcmp0(policy, "disable") != 0)
&& (g_strcmp0(policy, "legacy") != 0)) {
cons_show("TLS policy must be one of: force, allow, legacy or disable.");
} else {
accounts_set_tls_policy(account_name, policy);
cons_show("Updated TLS policy for account %s: %s", account_name, policy);
cons_show("");
}
return TRUE;
}
gboolean
_account_set_auth(char* account_name, char* policy)
{
if ((g_strcmp0(policy, "default") != 0)
&& (g_strcmp0(policy, "legacy") != 0)) {
cons_show("Auth policy must be either default or legacy.");
} else {
accounts_set_auth_policy(account_name, policy);
cons_show("Updated auth policy for account %s: %s", account_name, policy);
cons_show("");
}
return TRUE;
}
gboolean
_account_set_presence_priority(char* account_name, char* presence, char* priority)
{
int intval;
char* err_msg = NULL;
gboolean res = strtoi_range(priority, &intval, -128, 127, &err_msg);
if (!res) {
cons_show(err_msg);
free(err_msg);
return TRUE;
}
resource_presence_t presence_type = resource_presence_from_string(presence);
switch (presence_type) {
case (RESOURCE_ONLINE):
accounts_set_priority_online(account_name, intval);
break;
case (RESOURCE_CHAT):
accounts_set_priority_chat(account_name, intval);
break;
case (RESOURCE_AWAY):
accounts_set_priority_away(account_name, intval);
break;
case (RESOURCE_XA):
accounts_set_priority_xa(account_name, intval);
break;
case (RESOURCE_DND):
accounts_set_priority_dnd(account_name, intval);
break;
}
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status == JABBER_CONNECTED) {
char* connected_account = session_get_account_name();
resource_presence_t last_presence = accounts_get_last_presence(connected_account);
if (presence_type == last_presence) {
cl_ev_presence_send(last_presence, 0);
}
}
cons_show("Updated %s priority for account %s: %s", presence, account_name, priority);
cons_show("");
return TRUE;
}
gboolean
cmd_account_set(ProfWin* window, const char* const command, gchar** args)
{
if (g_strv_length(args) != 4) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* account_name = args[1];
if (!accounts_account_exists(account_name)) {
cons_show("Account %s doesn't exist", account_name);
cons_show("");
return TRUE;
}
char* property = args[2];
char* value = args[3];
if (strcmp(property, "jid") == 0)
return _account_set_jid(account_name, value);
if (strcmp(property, "server") == 0)
return _account_set_server(account_name, value);
if (strcmp(property, "port") == 0)
return _account_set_port(account_name, value);
if (strcmp(property, "resource") == 0)
return _account_set_resource(account_name, value);
if (strcmp(property, "password") == 0)
return _account_set_password(account_name, value);
if (strcmp(property, "eval_password") == 0)
return _account_set_eval_password(account_name, value);
if (strcmp(property, "muc") == 0)
return _account_set_muc(account_name, value);
if (strcmp(property, "nick") == 0)
return _account_set_nick(account_name, value);
if (strcmp(property, "otr") == 0)
return _account_set_otr(account_name, value);
if (strcmp(property, "status") == 0)
return _account_set_status(account_name, value);
if (strcmp(property, "pgpkeyid") == 0)
return _account_set_pgpkeyid(account_name, value);
if (strcmp(property, "startscript") == 0)
return _account_set_startscript(account_name, value);
if (strcmp(property, "theme") == 0)
return _account_set_theme(account_name, value);
if (strcmp(property, "tls") == 0)
return _account_set_tls(account_name, value);
if (strcmp(property, "auth") == 0)
return _account_set_auth(account_name, value);
if (valid_resource_presence_string(property)) {
return _account_set_presence_priority(account_name, property, value);
}
cons_show("Invalid property: %s", property);
cons_show("");
return TRUE;
}
gboolean
cmd_account_clear(ProfWin* window, const char* const command, gchar** args)
{
if (g_strv_length(args) != 3) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* account_name = args[1];
if (!accounts_account_exists(account_name)) {
cons_show("Account %s doesn't exist", account_name);
cons_show("");
return TRUE;
}
char* property = args[2];
if (strcmp(property, "password") == 0) {
accounts_clear_password(account_name);
cons_show("Removed password for account %s", account_name);
cons_show("");
} else if (strcmp(property, "eval_password") == 0) {
accounts_clear_eval_password(account_name);
cons_show("Removed eval password for account %s", account_name);
cons_show("");
} else if (strcmp(property, "server") == 0) {
accounts_clear_server(account_name);
cons_show("Removed server for account %s", account_name);
cons_show("");
} else if (strcmp(property, "port") == 0) {
accounts_clear_port(account_name);
cons_show("Removed port for account %s", account_name);
cons_show("");
} else if (strcmp(property, "otr") == 0) {
accounts_clear_otr(account_name);
cons_show("OTR policy removed for account %s", account_name);
cons_show("");
} else if (strcmp(property, "pgpkeyid") == 0) {
accounts_clear_pgp_keyid(account_name);
cons_show("Removed PGP key ID for account %s", account_name);
cons_show("");
} else if (strcmp(property, "startscript") == 0) {
accounts_clear_script_start(account_name);
cons_show("Removed start script for account %s", account_name);
cons_show("");
} else if (strcmp(property, "theme") == 0) {
accounts_clear_theme(account_name);
cons_show("Removed theme for account %s", account_name);
cons_show("");
} else if (strcmp(property, "muc") == 0) {
accounts_clear_muc(account_name);
cons_show("Removed MUC service for account %s", account_name);
cons_show("");
} else if (strcmp(property, "resource") == 0) {
accounts_clear_resource(account_name);
cons_show("Removed resource for account %s", account_name);
cons_show("");
} else {
cons_show("Invalid property: %s", property);
cons_show("");
}
return TRUE;
}
gboolean
cmd_account(ProfWin* window, const char* const command, gchar** args)
{
if (args[0] != NULL) {
cons_bad_cmd_usage(command);
cons_show("");
return TRUE;
}
if (connection_get_status() != JABBER_CONNECTED) {
cons_bad_cmd_usage(command);
return TRUE;
}
ProfAccount* account = accounts_get_account(session_get_account_name());
if (account) {
cons_show_account(account);
account_free(account);
} else {
log_error("Could not get accounts");
}
return TRUE;
}
gboolean
cmd_script(ProfWin* window, const char* const command, gchar** args)
{
if ((g_strcmp0(args[0], "run") == 0) && args[1]) {
gboolean res = scripts_exec(args[1]);
if (!res) {
cons_show("Could not find script %s", args[1]);
}
} else if (g_strcmp0(args[0], "list") == 0) {
GSList* scripts = scripts_list();
cons_show_scripts(scripts);
g_slist_free_full(scripts, g_free);
} else if ((g_strcmp0(args[0], "show") == 0) && args[1]) {
GSList* commands = scripts_read(args[1]);
cons_show_script(args[1], commands);
g_slist_free_full(commands, g_free);
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
}
/* escape a string into csv and write it to the file descriptor */
static int
_writecsv(int fd, const char* const str)
{
if (!str)
return 0;
size_t len = strlen(str);
char* s = malloc(2 * len * sizeof(char));
char* c = s;
for (int i = 0; i < strlen(str); i++) {
if (str[i] != '"')
*c++ = str[i];
else {
*c++ = '"';
*c++ = '"';
len++;
}
}
if (-1 == write(fd, s, len)) {
cons_show("error: failed to write '%s' to the requested file: %s", s, strerror(errno));
return -1;
}
free(s);
return 0;
}
gboolean
cmd_export(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
cons_show("");
return TRUE;
} else {
int fd;
GSList* list = NULL;
char* path = get_expanded_path(args[0]);
fd = open(path, O_WRONLY | O_CREAT, 00600);
if (-1 == fd) {
cons_show("error: cannot open %s: %s", args[0], strerror(errno));
cons_show("");
return TRUE;
}
if (-1 == write(fd, "jid,name\n", strlen("jid,name\n")))
goto write_error;
list = roster_get_contacts(ROSTER_ORD_NAME);
if (list) {
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
const char* jid = p_contact_barejid(contact);
const char* name = p_contact_name(contact);
/* write the data to the file */
if (-1 == write(fd, "\"", 1))
goto write_error;
if (-1 == _writecsv(fd, jid))
goto write_error;
if (-1 == write(fd, "\",\"", 3))
goto write_error;
if (-1 == _writecsv(fd, name))
goto write_error;
if (-1 == write(fd, "\"\n", 2))
goto write_error;
/* loop */
curr = g_slist_next(curr);
}
cons_show("Contacts exported successfully");
cons_show("");
} else {
cons_show("No contacts in roster.");
cons_show("");
}
g_slist_free(list);
close(fd);
return TRUE;
write_error:
cons_show("error: write failed: %s", strerror(errno));
cons_show("");
g_slist_free(list);
close(fd);
return TRUE;
}
}
gboolean
cmd_sub(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are currently not connected.");
return TRUE;
}
char *subcmd, *jid;
subcmd = args[0];
jid = args[1];
if (subcmd == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
if (strcmp(subcmd, "sent") == 0) {
cons_show_sent_subs();
return TRUE;
}
if (strcmp(subcmd, "received") == 0) {
cons_show_received_subs();
return TRUE;
}
if ((window->type != WIN_CHAT) && (jid == NULL)) {
cons_show("You must specify a contact.");
return TRUE;
}
if (jid == NULL) {
ProfChatWin* chatwin = (ProfChatWin*)window;
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
jid = chatwin->barejid;
}
Jid* jidp = jid_create(jid);
if (strcmp(subcmd, "allow") == 0) {
presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBED);
cons_show("Accepted subscription for %s", jidp->barejid);
log_info("Accepted subscription for %s", jidp->barejid);
} else if (strcmp(subcmd, "deny") == 0) {
presence_subscription(jidp->barejid, PRESENCE_UNSUBSCRIBED);
cons_show("Deleted/denied subscription for %s", jidp->barejid);
log_info("Deleted/denied subscription for %s", jidp->barejid);
} else if (strcmp(subcmd, "request") == 0) {
presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBE);
cons_show("Sent subscription request to %s.", jidp->barejid);
log_info("Sent subscription request to %s.", jidp->barejid);
} else if (strcmp(subcmd, "show") == 0) {
PContact contact = roster_get_contact(jidp->barejid);
if ((contact == NULL) || (p_contact_subscription(contact) == NULL)) {
if (window->type == WIN_CHAT) {
win_println(window, THEME_DEFAULT, "-", "No subscription information for %s.", jidp->barejid);
} else {
cons_show("No subscription information for %s.", jidp->barejid);
}
} else {
if (window->type == WIN_CHAT) {
if (p_contact_pending_out(contact)) {
win_println(window, THEME_DEFAULT, "-", "%s subscription status: %s, request pending.",
jidp->barejid, p_contact_subscription(contact));
} else {
win_println(window, THEME_DEFAULT, "-", "%s subscription status: %s.", jidp->barejid,
p_contact_subscription(contact));
}
} else {
if (p_contact_pending_out(contact)) {
cons_show("%s subscription status: %s, request pending.",
jidp->barejid, p_contact_subscription(contact));
} else {
cons_show("%s subscription status: %s.", jidp->barejid,
p_contact_subscription(contact));
}
}
}
} else {
cons_bad_cmd_usage(command);
}
jid_destroy(jidp);
return TRUE;
}
gboolean
cmd_disconnect(ProfWin* window, const char* const command, gchar** args)
{
if (connection_get_status() != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
cl_ev_disconnect();
ui_redraw();
return TRUE;
}
gboolean
cmd_quit(ProfWin* window, const char* const command, gchar** args)
{
log_info("Profanity is shutting down...");
exit(0);
return FALSE;
}
gboolean
cmd_wins_unread(ProfWin* window, const char* const command, gchar** args)
{
cons_show_wins(TRUE);
return TRUE;
}
gboolean
cmd_wins_attention(ProfWin* window, const char* const command, gchar** args)
{
cons_show_wins_attention();
return TRUE;
}
gboolean
cmd_wins_prune(ProfWin* window, const char* const command, gchar** args)
{
ui_prune_wins();
return TRUE;
}
gboolean
cmd_wins_swap(ProfWin* window, const char* const command, gchar** args)
{
if ((args[1] == NULL) || (args[2] == NULL)) {
cons_bad_cmd_usage(command);
return TRUE;
}
int source_win = atoi(args[1]);
int target_win = atoi(args[2]);
if ((source_win == 1) || (target_win == 1)) {
cons_show("Cannot move console window.");
return TRUE;
}
if (source_win == 10 || target_win == 10) {
cons_show("Window 10 does not exist");
return TRUE;
}
if (source_win == target_win) {
cons_show("Same source and target window supplied.");
return TRUE;
}
if (wins_get_by_num(source_win) == NULL) {
cons_show("Window %d does not exist", source_win);
return TRUE;
}
if (wins_get_by_num(target_win) == NULL) {
cons_show("Window %d does not exist", target_win);
return TRUE;
}
wins_swap(source_win, target_win);
cons_show("Swapped windows %d <-> %d", source_win, target_win);
return TRUE;
}
gboolean
cmd_wins(ProfWin* window, const char* const command, gchar** args)
{
if (args[0] != NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
cons_show_wins(FALSE);
return TRUE;
}
gboolean
cmd_close(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (g_strcmp0(args[0], "all") == 0) {
int count = ui_close_all_wins();
if (count == 0) {
cons_show("No windows to close.");
} else if (count == 1) {
cons_show("Closed 1 window.");
} else {
cons_show("Closed %d windows.", count);
}
rosterwin_roster();
return TRUE;
}
if (g_strcmp0(args[0], "read") == 0) {
int count = ui_close_read_wins();
if (count == 0) {
cons_show("No windows to close.");
} else if (count == 1) {
cons_show("Closed 1 window.");
} else {
cons_show("Closed %d windows.", count);
}
rosterwin_roster();
return TRUE;
}
gboolean is_num = TRUE;
int index = 0;
if (args[0] != NULL) {
for (int i = 0; i < strlen(args[0]); i++) {
if (!isdigit((int)args[0][i])) {
is_num = FALSE;
break;
}
}
if (is_num) {
index = atoi(args[0]);
}
} else {
index = wins_get_current_num();
}
if (is_num) {
if (index < 0 || index == 10) {
cons_show("No such window exists.");
return TRUE;
}
if (index == 1) {
cons_show("Cannot close console window.");
return TRUE;
}
ProfWin* toclose = wins_get_by_num(index);
if (!toclose) {
cons_show("Window is not open.");
return TRUE;
}
// check for unsaved form
if (ui_win_has_unsaved_form(index)) {
win_println(window, THEME_DEFAULT, "-", "You have unsaved changes, use /form submit or /form cancel");
return TRUE;
}
// handle leaving rooms, or chat
if (conn_status == JABBER_CONNECTED) {
ui_close_connected_win(index);
}
// close the window
ui_close_win(index);
cons_show("Closed window %d", index);
wins_tidy();
rosterwin_roster();
return TRUE;
} else {
if (g_strcmp0(args[0], "console") == 0) {
cons_show("Cannot close console window.");
return TRUE;
}
ProfWin* toclose = wins_get_by_string(args[0]);
if (!toclose) {
cons_show("Window \"%s\" does not exist.", args[0]);
return TRUE;
}
index = wins_get_num(toclose);
// check for unsaved form
if (ui_win_has_unsaved_form(index)) {
win_println(window, THEME_DEFAULT, "-", "You have unsaved changes, use /form submit or /form cancel");
return TRUE;
}
// handle leaving rooms, or chat
if (conn_status == JABBER_CONNECTED) {
ui_close_connected_win(index);
}
// close the window
ui_close_win(index);
cons_show("Closed window %s", args[0]);
wins_tidy();
rosterwin_roster();
return TRUE;
}
}
gboolean
cmd_win(ProfWin* window, const char* const command, gchar** args)
{
gboolean is_num = TRUE;
for (int i = 0; i < strlen(args[0]); i++) {
if (!isdigit((int)args[0][i])) {
is_num = FALSE;
break;
}
}
if (is_num) {
int num = atoi(args[0]);
ProfWin* focuswin = wins_get_by_num(num);
if (!focuswin) {
cons_show("Window %d does not exist.", num);
} else {
ui_focus_win(focuswin);
}
} else {
ProfWin* focuswin = wins_get_by_string(args[0]);
if (!focuswin) {
cons_show("Window \"%s\" does not exist.", args[0]);
} else {
ui_focus_win(focuswin);
}
}
return TRUE;
}
static void
_cmd_list_commands(GList* commands)
{
int maxlen = 0;
GList* curr = commands;
while (curr) {
gchar* cmd = curr->data;
int len = strlen(cmd);
if (len > maxlen)
maxlen = len;
curr = g_list_next(curr);
}
GString* cmds = g_string_new("");
curr = commands;
int count = 0;
while (curr) {
gchar* cmd = curr->data;
if (count == 5) {
cons_show(cmds->str);
g_string_free(cmds, TRUE);
cmds = g_string_new("");
count = 0;
}
g_string_append_printf(cmds, "%-*s", maxlen + 1, cmd);
curr = g_list_next(curr);
count++;
}
cons_show(cmds->str);
g_string_free(cmds, TRUE);
g_list_free(curr);
cons_show("");
cons_show("Use /help [command] without the leading slash, for help on a specific command");
cons_show("");
}
static void
_cmd_help_cmd_list(const char* const tag)
{
cons_show("");
ProfWin* console = wins_get_console();
if (tag) {
win_println(console, THEME_HELP_HEADER, "-", "%s commands", tag);
} else {
win_println(console, THEME_HELP_HEADER, "-", "All commands");
}
GList* ordered_commands = NULL;
if (g_strcmp0(tag, "plugins") == 0) {
GList* plugins_cmds = plugins_get_command_names();
GList* curr = plugins_cmds;
while (curr) {
ordered_commands = g_list_insert_sorted(ordered_commands, curr->data, (GCompareFunc)g_strcmp0);
curr = g_list_next(curr);
}
g_list_free(plugins_cmds);
} else {
ordered_commands = cmd_get_ordered(tag);
// add plugins if showing all commands
if (!tag) {
GList* plugins_cmds = plugins_get_command_names();
GList* curr = plugins_cmds;
while (curr) {
ordered_commands = g_list_insert_sorted(ordered_commands, curr->data, (GCompareFunc)g_strcmp0);
curr = g_list_next(curr);
}
g_list_free(plugins_cmds);
}
}
_cmd_list_commands(ordered_commands);
g_list_free(ordered_commands);
}
gboolean
cmd_help(ProfWin* window, const char* const command, gchar** args)
{
int num_args = g_strv_length(args);
if (num_args == 0) {
cons_help();
} else if (strcmp(args[0], "search_all") == 0) {
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
} else {
GList* cmds = cmd_search_index_all(args[1]);
if (cmds == NULL) {
cons_show("No commands found.");
} else {
GList* curr = cmds;
GList* results = NULL;
while (curr) {
results = g_list_insert_sorted(results, curr->data, (GCompareFunc)g_strcmp0);
curr = g_list_next(curr);
}
cons_show("Search results:");
_cmd_list_commands(results);
g_list_free(results);
}
g_list_free(cmds);
}
} else if (strcmp(args[0], "search_any") == 0) {
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
} else {
GList* cmds = cmd_search_index_any(args[1]);
if (cmds == NULL) {
cons_show("No commands found.");
} else {
GList* curr = cmds;
GList* results = NULL;
while (curr) {
results = g_list_insert_sorted(results, curr->data, (GCompareFunc)g_strcmp0);
curr = g_list_next(curr);
}
cons_show("Search results:");
_cmd_list_commands(results);
g_list_free(results);
}
g_list_free(cmds);
}
} else if (strcmp(args[0], "commands") == 0) {
if (args[1]) {
if (!cmd_valid_tag(args[1])) {
cons_bad_cmd_usage(command);
} else {
_cmd_help_cmd_list(args[1]);
}
} else {
_cmd_help_cmd_list(NULL);
}
} else if (strcmp(args[0], "navigation") == 0) {
cons_navigation_help();
} else {
char* cmd = args[0];
char* cmd_with_slash = g_strdup_printf("/%s", cmd);
Command* command = cmd_get(cmd_with_slash);
if (command) {
cons_show_help(cmd_with_slash, &command->help);
} else {
CommandHelp* commandHelp = plugins_get_help(cmd_with_slash);
if (commandHelp) {
cons_show_help(cmd_with_slash, commandHelp);
} else {
cons_show("No such command.");
}
}
cons_show("");
g_free(cmd_with_slash);
}
return TRUE;
}
gboolean
cmd_about(ProfWin* window, const char* const command, gchar** args)
{
cons_show("");
cons_about();
return TRUE;
}
gboolean
cmd_prefs(ProfWin* window, const char* const command, gchar** args)
{
if (args[0] == NULL) {
cons_prefs();
cons_show("Use the /account command for preferences for individual accounts.");
} else if (strcmp(args[0], "ui") == 0) {
cons_show("");
cons_show_ui_prefs();
cons_show("");
} else if (strcmp(args[0], "desktop") == 0) {
cons_show("");
cons_show_desktop_prefs();
cons_show("");
} else if (strcmp(args[0], "chat") == 0) {
cons_show("");
cons_show_chat_prefs();
cons_show("");
} else if (strcmp(args[0], "log") == 0) {
cons_show("");
cons_show_log_prefs();
cons_show("");
} else if (strcmp(args[0], "conn") == 0) {
cons_show("");
cons_show_connection_prefs();
cons_show("");
} else if (strcmp(args[0], "presence") == 0) {
cons_show("");
cons_show_presence_prefs();
cons_show("");
} else if (strcmp(args[0], "otr") == 0) {
cons_show("");
cons_show_otr_prefs();
cons_show("");
} else if (strcmp(args[0], "pgp") == 0) {
cons_show("");
cons_show_pgp_prefs();
cons_show("");
} else if (strcmp(args[0], "omemo") == 0) {
cons_show("");
cons_show_omemo_prefs();
cons_show("");
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
}
gboolean
cmd_theme(ProfWin* window, const char* const command, gchar** args)
{
// 'full-load' means to load the theme including the settings (not just [colours])
gboolean fullload = (g_strcmp0(args[0], "full-load") == 0);
// list themes
if (g_strcmp0(args[0], "list") == 0) {
GSList* themes = theme_list();
cons_show_themes(themes);
g_slist_free_full(themes, g_free);
// load a theme
} else if (g_strcmp0(args[0], "load") == 0 || fullload) {
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
} else if (theme_load(args[1], fullload)) {
ui_load_colours();
prefs_set_string(PREF_THEME, args[1]);
if (prefs_get_boolean(PREF_ROSTER)) {
ui_show_roster();
} else {
ui_hide_roster();
}
if (prefs_get_boolean(PREF_OCCUPANTS)) {
ui_show_all_room_rosters();
} else {
ui_hide_all_room_rosters();
}
ui_resize();
cons_show("Loaded theme: %s", args[1]);
} else {
cons_show("Couldn't find theme: %s", args[1]);
}
// show colours
} else if (g_strcmp0(args[0], "colours") == 0) {
cons_theme_colours();
} else if (g_strcmp0(args[0], "properties") == 0) {
cons_theme_properties();
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
}
static void
_who_room(ProfWin* window, const char* const command, gchar** args)
{
if ((g_strv_length(args) == 2) && args[1]) {
cons_show("Argument group is not applicable to chat rooms.");
return;
}
// bad arg
if (args[0] && (g_strcmp0(args[0], "online") != 0) && (g_strcmp0(args[0], "available") != 0) && (g_strcmp0(args[0], "unavailable") != 0) && (g_strcmp0(args[0], "away") != 0) && (g_strcmp0(args[0], "chat") != 0) && (g_strcmp0(args[0], "xa") != 0) && (g_strcmp0(args[0], "dnd") != 0) && (g_strcmp0(args[0], "any") != 0) && (g_strcmp0(args[0], "moderator") != 0) && (g_strcmp0(args[0], "participant") != 0) && (g_strcmp0(args[0], "visitor") != 0) && (g_strcmp0(args[0], "owner") != 0) && (g_strcmp0(args[0], "admin") != 0) && (g_strcmp0(args[0], "member") != 0) && (g_strcmp0(args[0], "outcast") != 0)) {
cons_bad_cmd_usage(command);
return;
}
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
// presence filter
if (args[0] == NULL || (g_strcmp0(args[0], "online") == 0) || (g_strcmp0(args[0], "available") == 0) || (g_strcmp0(args[0], "unavailable") == 0) || (g_strcmp0(args[0], "away") == 0) || (g_strcmp0(args[0], "chat") == 0) || (g_strcmp0(args[0], "xa") == 0) || (g_strcmp0(args[0], "dnd") == 0) || (g_strcmp0(args[0], "any") == 0)) {
char* presence = args[0];
GList* occupants = muc_roster(mucwin->roomjid);
// no arg, show all contacts
if ((presence == NULL) || (g_strcmp0(presence, "any") == 0)) {
mucwin_roster(mucwin, occupants, NULL);
// available
} else if (strcmp("available", presence) == 0) {
GList* filtered = NULL;
while (occupants) {
Occupant* occupant = occupants->data;
if (muc_occupant_available(occupant)) {
filtered = g_list_append(filtered, occupant);
}
occupants = g_list_next(occupants);
}
mucwin_roster(mucwin, filtered, "available");
// unavailable
} else if (strcmp("unavailable", presence) == 0) {
GList* filtered = NULL;
while (occupants) {
Occupant* occupant = occupants->data;
if (!muc_occupant_available(occupant)) {
filtered = g_list_append(filtered, occupant);
}
occupants = g_list_next(occupants);
}
mucwin_roster(mucwin, filtered, "unavailable");
// show specific status
} else {
GList* filtered = NULL;
while (occupants) {
Occupant* occupant = occupants->data;
const char* presence_str = string_from_resource_presence(occupant->presence);
if (strcmp(presence_str, presence) == 0) {
filtered = g_list_append(filtered, occupant);
}
occupants = g_list_next(occupants);
}
mucwin_roster(mucwin, filtered, presence);
}
g_list_free(occupants);
// role or affiliation filter
} else {
if (g_strcmp0(args[0], "moderator") == 0) {
mucwin_show_role_list(mucwin, MUC_ROLE_MODERATOR);
return;
}
if (g_strcmp0(args[0], "participant") == 0) {
mucwin_show_role_list(mucwin, MUC_ROLE_PARTICIPANT);
return;
}
if (g_strcmp0(args[0], "visitor") == 0) {
mucwin_show_role_list(mucwin, MUC_ROLE_VISITOR);
return;
}
if (g_strcmp0(args[0], "owner") == 0) {
mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_OWNER);
return;
}
if (g_strcmp0(args[0], "admin") == 0) {
mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_ADMIN);
return;
}
if (g_strcmp0(args[0], "member") == 0) {
mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_MEMBER);
return;
}
if (g_strcmp0(args[0], "outcast") == 0) {
mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_OUTCAST);
return;
}
}
}
static void
_who_roster(ProfWin* window, const char* const command, gchar** args)
{
char* presence = args[0];
// bad arg
if (presence
&& (strcmp(presence, "online") != 0)
&& (strcmp(presence, "available") != 0)
&& (strcmp(presence, "unavailable") != 0)
&& (strcmp(presence, "offline") != 0)
&& (strcmp(presence, "away") != 0)
&& (strcmp(presence, "chat") != 0)
&& (strcmp(presence, "xa") != 0)
&& (strcmp(presence, "dnd") != 0)
&& (strcmp(presence, "any") != 0)) {
cons_bad_cmd_usage(command);
return;
}
char* group = NULL;
if ((g_strv_length(args) == 2) && args[1]) {
group = args[1];
}
cons_show("");
GSList* list = NULL;
if (group) {
list = roster_get_group(group, ROSTER_ORD_NAME);
if (list == NULL) {
cons_show("No such group: %s.", group);
return;
}
} else {
list = roster_get_contacts(ROSTER_ORD_NAME);
if (list == NULL) {
cons_show("No contacts in roster.");
return;
}
}
// no arg, show all contacts
if ((presence == NULL) || (g_strcmp0(presence, "any") == 0)) {
if (group) {
if (list == NULL) {
cons_show("No contacts in group %s.", group);
} else {
cons_show("%s:", group);
cons_show_contacts(list);
}
} else {
if (list == NULL) {
cons_show("You have no contacts.");
} else {
cons_show("All contacts:");
cons_show_contacts(list);
}
}
// available
} else if (strcmp("available", presence) == 0) {
GSList* filtered = NULL;
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
if (p_contact_is_available(contact)) {
filtered = g_slist_append(filtered, contact);
}
curr = g_slist_next(curr);
}
if (group) {
if (filtered == NULL) {
cons_show("No contacts in group %s are %s.", group, presence);
} else {
cons_show("%s (%s):", group, presence);
cons_show_contacts(filtered);
}
} else {
if (filtered == NULL) {
cons_show("No contacts are %s.", presence);
} else {
cons_show("Contacts (%s):", presence);
cons_show_contacts(filtered);
}
}
g_slist_free(filtered);
// unavailable
} else if (strcmp("unavailable", presence) == 0) {
GSList* filtered = NULL;
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
if (!p_contact_is_available(contact)) {
filtered = g_slist_append(filtered, contact);
}
curr = g_slist_next(curr);
}
if (group) {
if (filtered == NULL) {
cons_show("No contacts in group %s are %s.", group, presence);
} else {
cons_show("%s (%s):", group, presence);
cons_show_contacts(filtered);
}
} else {
if (filtered == NULL) {
cons_show("No contacts are %s.", presence);
} else {
cons_show("Contacts (%s):", presence);
cons_show_contacts(filtered);
}
}
g_slist_free(filtered);
// online, available resources
} else if (strcmp("online", presence) == 0) {
GSList* filtered = NULL;
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
if (p_contact_has_available_resource(contact)) {
filtered = g_slist_append(filtered, contact);
}
curr = g_slist_next(curr);
}
if (group) {
if (filtered == NULL) {
cons_show("No contacts in group %s are %s.", group, presence);
} else {
cons_show("%s (%s):", group, presence);
cons_show_contacts(filtered);
}
} else {
if (filtered == NULL) {
cons_show("No contacts are %s.", presence);
} else {
cons_show("Contacts (%s):", presence);
cons_show_contacts(filtered);
}
}
g_slist_free(filtered);
// offline, no available resources
} else if (strcmp("offline", presence) == 0) {
GSList* filtered = NULL;
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
if (!p_contact_has_available_resource(contact)) {
filtered = g_slist_append(filtered, contact);
}
curr = g_slist_next(curr);
}
if (group) {
if (filtered == NULL) {
cons_show("No contacts in group %s are %s.", group, presence);
} else {
cons_show("%s (%s):", group, presence);
cons_show_contacts(filtered);
}
} else {
if (filtered == NULL) {
cons_show("No contacts are %s.", presence);
} else {
cons_show("Contacts (%s):", presence);
cons_show_contacts(filtered);
}
}
g_slist_free(filtered);
// show specific status
} else {
GSList* filtered = NULL;
GSList* curr = list;
while (curr) {
PContact contact = curr->data;
if (strcmp(p_contact_presence(contact), presence) == 0) {
filtered = g_slist_append(filtered, contact);
}
curr = g_slist_next(curr);
}
if (group) {
if (filtered == NULL) {
cons_show("No contacts in group %s are %s.", group, presence);
} else {
cons_show("%s (%s):", group, presence);
cons_show_contacts(filtered);
}
} else {
if (filtered == NULL) {
cons_show("No contacts are %s.", presence);
} else {
cons_show("Contacts (%s):", presence);
cons_show_contacts(filtered);
}
}
g_slist_free(filtered);
}
g_slist_free(list);
}
gboolean
cmd_who(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
} else if (window->type == WIN_MUC) {
_who_room(window, command, args);
} else {
_who_roster(window, command, args);
}
if (window->type != WIN_CONSOLE && window->type != WIN_MUC) {
status_bar_new(1, WIN_CONSOLE, "console");
}
return TRUE;
}
static void
_cmd_msg_chatwin(const char* const barejid, const char* const msg)
{
ProfChatWin* chatwin = wins_get_chat(barejid);
if (!chatwin) {
// NOTE: This will also start the new OMEMO session and send a MAM request.
chatwin = chatwin_new(barejid);
}
ui_focus_win((ProfWin*)chatwin);
if (msg) {
// NOTE: In case the message is OMEMO encrypted, we can't be sure
// whether the key bundles of the recipient have already been
// received. In the case that *no* bundles have been received yet,
// the message won't be sent, and an error is shown to the user.
// Other cases are not handled here.
cl_ev_send_msg(chatwin, msg, NULL);
} else {
#ifdef HAVE_LIBOTR
// Start the OTR session after this (i.e. the first) message was sent
if (otr_is_secure(barejid)) {
chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
}
#endif // HAVE_LIBOTR
}
}
gboolean
cmd_msg(ProfWin* window, const char* const command, gchar** args)
{
char* usr = args[0];
char* msg = args[1];
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
// send private message when in MUC room
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Occupant* occupant = muc_roster_item(mucwin->roomjid, usr);
if (occupant) {
// in case of non-anon muc send regular chatmessage
if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS) {
Jid* jidp = jid_create(occupant->jid);
_cmd_msg_chatwin(jidp->barejid, msg);
win_println(window, THEME_DEFAULT, "-", "Starting direct message with occupant \"%s\" from room \"%s\" as \"%s\".", usr, mucwin->roomjid, jidp->barejid);
cons_show("Starting direct message with occupant \"%s\" from room \"%s\" as \"%s\".", usr, mucwin->roomjid, jidp->barejid);
jid_destroy(jidp);
} else {
// otherwise send mucpm
GString* full_jid = g_string_new(mucwin->roomjid);
g_string_append(full_jid, "/");
g_string_append(full_jid, usr);
ProfPrivateWin* privwin = wins_get_private(full_jid->str);
if (!privwin) {
privwin = (ProfPrivateWin*)wins_new_private(full_jid->str);
}
ui_focus_win((ProfWin*)privwin);
if (msg) {
cl_ev_send_priv_msg(privwin, msg, NULL);
}
g_string_free(full_jid, TRUE);
}
} else {
win_println(window, THEME_DEFAULT, "-", "No such participant \"%s\" in room.", usr);
}
return TRUE;
// send chat message
} else {
char* barejid = roster_barejid_from_name(usr);
if (barejid == NULL) {
barejid = usr;
}
_cmd_msg_chatwin(barejid, msg);
return TRUE;
}
}
gboolean
cmd_group(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
// list all groups
if (args[1] == NULL) {
GList* groups = roster_get_groups();
GList* curr = groups;
if (curr) {
cons_show("Groups:");
while (curr) {
cons_show(" %s", curr->data);
curr = g_list_next(curr);
}
g_list_free_full(groups, g_free);
} else {
cons_show("No groups.");
}
return TRUE;
}
// show contacts in group
if (strcmp(args[1], "show") == 0) {
char* group = args[2];
if (group == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
GSList* list = roster_get_group(group, ROSTER_ORD_NAME);
cons_show_roster_group(group, list);
return TRUE;
}
// add contact to group
if (strcmp(args[1], "add") == 0) {
char* group = args[2];
char* contact = args[3];
if ((group == NULL) || (contact == NULL)) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* barejid = roster_barejid_from_name(contact);
if (barejid == NULL) {
barejid = contact;
}
PContact pcontact = roster_get_contact(barejid);
if (pcontact == NULL) {
cons_show("Contact not found in roster: %s", barejid);
return TRUE;
}
if (p_contact_in_group(pcontact, group)) {
const char* display_name = p_contact_name_or_jid(pcontact);
ui_contact_already_in_group(display_name, group);
} else {
roster_send_add_to_group(group, pcontact);
}
return TRUE;
}
// remove contact from group
if (strcmp(args[1], "remove") == 0) {
char* group = args[2];
char* contact = args[3];
if ((group == NULL) || (contact == NULL)) {
cons_bad_cmd_usage(command);
return TRUE;
}
char* barejid = roster_barejid_from_name(contact);
if (barejid == NULL) {
barejid = contact;
}
PContact pcontact = roster_get_contact(barejid);
if (pcontact == NULL) {
cons_show("Contact not found in roster: %s", barejid);
return TRUE;
}
if (!p_contact_in_group(pcontact, group)) {
const char* display_name = p_contact_name_or_jid(pcontact);
ui_contact_not_in_group(display_name, group);
} else {
roster_send_remove_from_group(group, pcontact);
}
return TRUE;
}
cons_bad_cmd_usage(command);
return TRUE;
}
gboolean
cmd_roster(ProfWin* window, const char* const command, gchar** args)
{
jabber_conn_status_t conn_status = connection_get_status();
// show roster
if (args[0] == NULL) {
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
GSList* list = roster_get_contacts(ROSTER_ORD_NAME);
cons_show_roster(list);
g_slist_free(list);
return TRUE;
// show roster, only online contacts
} else if (g_strcmp0(args[0], "online") == 0) {
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
GSList* list = roster_get_contacts_online();
cons_show_roster(list);
g_slist_free(list);
return TRUE;
// set roster size
} else if (g_strcmp0(args[0], "size") == 0) {
if (!args[1]) {
cons_bad_cmd_usage(command);
return TRUE;
}
int intval = 0;
char* err_msg = NULL;
gboolean res = strtoi_range(args[1], &intval, 1, 99, &err_msg);
if (res) {
prefs_set_roster_size(intval);
cons_show("Roster screen size set to: %d%%", intval);
if (conn_status == JABBER_CONNECTED && prefs_get_boolean(PREF_ROSTER)) {
wins_resize_all();
}
return TRUE;
} else {
cons_show(err_msg);
free(err_msg);
return TRUE;
}
// set line wrapping
} else if (g_strcmp0(args[0], "wrap") == 0) {
if (!args[1]) {
cons_bad_cmd_usage(command);
return TRUE;
} else {
_cmd_set_boolean_preference(args[1], command, "Roster panel line wrap", PREF_ROSTER_WRAP);
rosterwin_roster();
return TRUE;
}
// header settings
} else if (g_strcmp0(args[0], "header") == 0) {
if (g_strcmp0(args[1], "char") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else if (g_strcmp0(args[2], "none") == 0) {
prefs_clear_roster_header_char();
cons_show("Roster header char removed.");
rosterwin_roster();
} else {
prefs_set_roster_header_char(args[2]);
cons_show("Roster header char set to %s.", args[2]);
rosterwin_roster();
}
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
// contact settings
} else if (g_strcmp0(args[0], "contact") == 0) {
if (g_strcmp0(args[1], "char") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else if (g_strcmp0(args[2], "none") == 0) {
prefs_clear_roster_contact_char();
cons_show("Roster contact char removed.");
rosterwin_roster();
} else {
prefs_set_roster_contact_char(args[2]);
cons_show("Roster contact char set to %s.", args[2]);
rosterwin_roster();
}
} else if (g_strcmp0(args[1], "indent") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else {
int intval = 0;
char* err_msg = NULL;
gboolean res = strtoi_range(args[2], &intval, 0, 10, &err_msg);
if (res) {
prefs_set_roster_contact_indent(intval);
cons_show("Roster contact indent set to: %d", intval);
rosterwin_roster();
} else {
cons_show(err_msg);
free(err_msg);
}
}
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
// resource settings
} else if (g_strcmp0(args[0], "resource") == 0) {
if (g_strcmp0(args[1], "char") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else if (g_strcmp0(args[2], "none") == 0) {
prefs_clear_roster_resource_char();
cons_show("Roster resource char removed.");
rosterwin_roster();
} else {
prefs_set_roster_resource_char(args[2]);
cons_show("Roster resource char set to %s.", args[2]);
rosterwin_roster();
}
} else if (g_strcmp0(args[1], "indent") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else {
int intval = 0;
char* err_msg = NULL;
gboolean res = strtoi_range(args[2], &intval, 0, 10, &err_msg);
if (res) {
prefs_set_roster_resource_indent(intval);
cons_show("Roster resource indent set to: %d", intval);
rosterwin_roster();
} else {
cons_show(err_msg);
free(err_msg);
}
}
} else if (g_strcmp0(args[1], "join") == 0) {
_cmd_set_boolean_preference(args[2], command, "Roster join", PREF_ROSTER_RESOURCE_JOIN);
rosterwin_roster();
return TRUE;
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
// presence settings
} else if (g_strcmp0(args[0], "presence") == 0) {
if (g_strcmp0(args[1], "indent") == 0) {
if (!args[2]) {
cons_bad_cmd_usage(command);
} else {
int intval = 0;
char* err_msg = NULL;
gboolean res = strtoi_range(args[2], &intval, -1, 10, &err_msg);
if (res) {
prefs_set_roster_presence_indent(intval);
cons_show("Roster presence indent set to: %d", intval);
rosterwin_roster();
} else {
cons_show(err_msg);
free(err_msg);
}
}
} else {
cons_bad_cmd_usage(command);
}
return TRUE;
// show/hide roster
} else if ((g_strcmp0(args[0], "show") == 0) || (g_strcmp0(args[0], "hide") == 0)) {
preference_t pref;
const char* pref_str;
if (args[1] == NULL) {
pref = PREF_ROSTER;
pref_str = "";
} else if (g_strcmp0(args[1], "offline") == 0) {
pref = PREF_ROSTER_OFFLINE;
pref_str = "offline";
} else if (g_strcmp0(args[1], "resource") == 0) {
pref = PREF_ROSTER_RESOURCE;
pref_str = "resource";
} else if (g_strcmp0(args[1], "presence") == 0) {
pref = PREF_ROSTER_PRESENCE;
pref_str = "presence";
} else if (g_strcmp0(args[1], "status") == 0) {
pref = PREF_ROSTER_STATUS;
pref_str = "status";
} else if (g_strcmp0(args[1], "empty") == 0) {
pref = PREF_ROSTER_EMPTY;
pref_str = "empty";
} else if (g_strcmp0(args[1], "priority") == 0) {
pref = PREF_ROSTER_PRIORITY;
pref_str = "priority";
} else if (g_strcmp0(args[1], "contacts") == 0) {
pref = PREF_ROSTER_CONTACTS;
pref_str = "contacts";
} else if (g_strcmp0(args[1], "rooms") == 0) {
pref = PREF_ROSTER_ROOMS;
pref_str = "rooms";
} else if (g_strcmp0(args[1], "unsubscribed") == 0) {
pref = PREF_ROSTER_UNSUBSCRIBED;
pref_str = "unsubscribed";
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
gboolean val;
if (g_strcmp0(args[0], "show") == 0) {
val = TRUE;
} else { // "hide"
val = FALSE;
}
cons_show("Roster%s%s %s (was %s)", strlen(pref_str) == 0 ? "" : " ", pref_str,
val == TRUE ? "enabled" : "disabled",
prefs_get_boolean(pref) == TRUE ? "enabled" : "disabled");
prefs_set_boolean(pref, val);
if (conn_status == JABBER_CONNECTED) {
if (pref == PREF_ROSTER) {
if (val == TRUE) {
ui_show_roster();
} else {
ui_hide_roster();
}
} else {
rosterwin_roster();
}
}
return TRUE;
// roster grouping
} else if (g_strcmp0(args[0], "by") == 0) {
if (g_strcmp0(args[1], "group") == 0) {
cons_show("Grouping roster by roster group");
prefs_set_string(PREF_ROSTER_BY, "group");
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else if (g_strcmp0(args[1], "presence") == 0) {
cons_show("Grouping roster by presence");
prefs_set_string(PREF_ROSTER_BY, "presence");
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else if (g_strcmp0(args[1], "none") == 0) {
cons_show("Roster grouping disabled");
prefs_set_string(PREF_ROSTER_BY, "none");
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
// roster item order
} else if (g_strcmp0(args[0], "order") == 0) {
if (g_strcmp0(args[1], "name") == 0) {
cons_show("Ordering roster by name");
prefs_set_string(PREF_ROSTER_ORDER, "name");
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else if (g_strcmp0(args[1], "presence") == 0) {
cons_show("Ordering roster by presence");
prefs_set_string(PREF_ROSTER_ORDER, "presence");
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
} else if (g_strcmp0(args[0], "count") == 0) {
if (g_strcmp0(args[1], "zero") == 0) {
_cmd_set_boolean_preference(args[2], command, "Roster header zero count", PREF_ROSTER_COUNT_ZERO);
if (conn_status == JABBER_CONNECTED) {
rosterwin_roster();
}
return TRUE;
} else if (g_strcmp0(args