1 /*
2 * cmd_funcs.c
3 * vim: expandtab:ts=4:sts=4:sw=4
4 *
5 * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
6 * Copyright (C) 2019 Michael Vetter <jubalh@iodoru.org>
7 * Copyright (C) 2020 William Wennerström <william@wstrm.dev>
8 *
9 * This file is part of Profanity.
10 *
11 * Profanity is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * Profanity is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Profanity. If not, see <https://www.gnu.org/licenses/>.
23 *
24 * In addition, as a special exception, the copyright holders give permission to
25 * link the code of portions of this program with the OpenSSL library under
26 * certain conditions as described in each individual source file, and
27 * distribute linked combinations including the two.
28 *
29 * You must obey the GNU General Public License in all respects for all of the
30 * code used other than OpenSSL. If you modify file(s) with this exception, you
31 * may extend this exception to your version of the file(s), but you are not
32 * obligated to do so. If you do not wish to do so, delete this exception
33 * statement from your version. If you delete this exception statement from all
34 * source files in the program, then also delete it here.
35 *
36 */
37
38 #include "config.h"
39
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <assert.h>
45 #include <glib.h>
46 #include <glib/gstdio.h>
47 #include <gio/gio.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <fcntl.h>
51 #include <unistd.h>
52 #include <langinfo.h>
53 #include <ctype.h>
54
55 // fork / execl
56 #include <sys/types.h>
57 #include <unistd.h>
58 #include <sys/wait.h>
59 #include <readline/readline.h>
60
61 #include "profanity.h"
62 #include "log.h"
63 #include "common.h"
64 #include "command/cmd_funcs.h"
65 #include "command/cmd_defs.h"
66 #include "command/cmd_ac.h"
67 #include "config/files.h"
68 #include "config/accounts.h"
69 #include "config/account.h"
70 #include "config/preferences.h"
71 #include "config/theme.h"
72 #include "config/tlscerts.h"
73 #include "config/scripts.h"
74 #include "event/client_events.h"
75 #include "tools/http_upload.h"
76 #include "tools/http_download.h"
77 #include "tools/autocomplete.h"
78 #include "tools/parser.h"
79 #include "tools/bookmark_ignore.h"
80 #include "plugins/plugins.h"
81 #include "ui/ui.h"
82 #include "ui/window_list.h"
83 #include "xmpp/xmpp.h"
84 #include "xmpp/connection.h"
85 #include "xmpp/contact.h"
86 #include "xmpp/roster_list.h"
87 #include "xmpp/jid.h"
88 #include "xmpp/muc.h"
89 #include "xmpp/chat_session.h"
90 #include "xmpp/avatar.h"
91
92 #ifdef HAVE_LIBOTR
93 #include "otr/otr.h"
94 #endif
95
96 #ifdef HAVE_LIBGPGME
97 #include "pgp/gpg.h"
98 #include "xmpp/ox.h"
99 #endif
100
101 #ifdef HAVE_OMEMO
102 #include "omemo/omemo.h"
103 #include "xmpp/omemo.h"
104 #include "tools/aesgcm_download.h"
105 #endif
106
107 #ifdef HAVE_GTK
108 #include "ui/tray.h"
109 #include "tools/clipboard.h"
110 #endif
111
112 #ifdef HAVE_PYTHON
113 #include "plugins/python_plugins.h"
114 #endif
115
116 static void _update_presence(const resource_presence_t presence,
117 const char* const show, gchar** args);
118 static void _cmd_set_boolean_preference(gchar* arg, const char* const command,
119 const char* const display, preference_t pref);
120 static void _who_room(ProfWin* window, const char* const command, gchar** args);
121 static void _who_roster(ProfWin* window, const char* const command, gchar** args);
122 static gboolean _cmd_execute(ProfWin* window, const char* const command, const char* const inp);
123 static gboolean _cmd_execute_default(ProfWin* window, const char* inp);
124 static gboolean _cmd_execute_alias(ProfWin* window, const char* const inp, gboolean* ran);
125
126 /*
127 * Take a line of input and process it, return TRUE if profanity is to
128 * continue, FALSE otherwise
129 */
130 gboolean
cmd_process_input(ProfWin * window,char * inp)131 cmd_process_input(ProfWin* window, char* inp)
132 {
133 log_debug("Input received: %s", inp);
134 gboolean result = FALSE;
135 g_strchomp(inp);
136
137 // just carry on if no input
138 if (strlen(inp) == 0) {
139 result = TRUE;
140
141 // handle command if input starts with a '/'
142 } else if (inp[0] == '/') {
143 char* inp_cpy = strdup(inp);
144 char* command = strtok(inp_cpy, " ");
145 char* question_mark = strchr(command, '?');
146 if (question_mark) {
147 *question_mark = '\0';
148 gchar* fakeinp = g_strdup_printf("/help %s", command + 1);
149 if (fakeinp) {
150 result = _cmd_execute(window, "/help", fakeinp);
151 g_free(fakeinp);
152 }
153 } else {
154 result = _cmd_execute(window, command, inp);
155 }
156 free(inp_cpy);
157
158 // call a default handler if input didn't start with '/'
159 } else {
160 result = _cmd_execute_default(window, inp);
161 }
162
163 return result;
164 }
165
166 // Command execution
167
168 void
cmd_execute_connect(ProfWin * window,const char * const account)169 cmd_execute_connect(ProfWin* window, const char* const account)
170 {
171 GString* command = g_string_new("/connect ");
172 g_string_append(command, account);
173 cmd_process_input(window, command->str);
174 g_string_free(command, TRUE);
175 }
176
177 gboolean
cmd_tls_certpath(ProfWin * window,const char * const command,gchar ** args)178 cmd_tls_certpath(ProfWin* window, const char* const command, gchar** args)
179 {
180 #ifdef HAVE_LIBMESODE
181 if (g_strcmp0(args[1], "set") == 0) {
182 if (args[2] == NULL) {
183 cons_bad_cmd_usage(command);
184 return TRUE;
185 }
186
187 if (g_file_test(args[2], G_FILE_TEST_IS_DIR)) {
188 prefs_set_string(PREF_TLS_CERTPATH, args[2]);
189 cons_show("Certificate path set to: %s", args[2]);
190 } else {
191 cons_show("Directory %s does not exist.", args[2]);
192 }
193 return TRUE;
194 } else if (g_strcmp0(args[1], "clear") == 0) {
195 prefs_set_string(PREF_TLS_CERTPATH, "none");
196 cons_show("Certificate path cleared");
197 return TRUE;
198 } else if (g_strcmp0(args[1], "default") == 0) {
199 prefs_set_string(PREF_TLS_CERTPATH, NULL);
200 cons_show("Certificate path defaulted to finding system certpath.");
201 return TRUE;
202 } else if (args[1] == NULL) {
203 char* path = prefs_get_tls_certpath();
204 if (path) {
205 cons_show("Trusted certificate path: %s", path);
206 free(path);
207 } else {
208 cons_show("No trusted certificate path set.");
209 }
210 return TRUE;
211 } else {
212 cons_bad_cmd_usage(command);
213 return TRUE;
214 }
215 #else
216 cons_show("Certificate path setting only supported when built with libmesode.");
217 return TRUE;
218 #endif
219 }
220
221 gboolean
cmd_tls_trust(ProfWin * window,const char * const command,gchar ** args)222 cmd_tls_trust(ProfWin* window, const char* const command, gchar** args)
223 {
224 #ifdef HAVE_LIBMESODE
225 jabber_conn_status_t conn_status = connection_get_status();
226 if (conn_status != JABBER_CONNECTED) {
227 cons_show("You are currently not connected.");
228 return TRUE;
229 }
230 if (!connection_is_secured()) {
231 cons_show("No TLS connection established");
232 return TRUE;
233 }
234 TLSCertificate* cert = connection_get_tls_peer_cert();
235 if (!cert) {
236 cons_show("Error getting TLS certificate.");
237 return TRUE;
238 }
239 if (tlscerts_exists(cert->fingerprint)) {
240 cons_show("Certificate %s already trusted.", cert->fingerprint);
241 tlscerts_free(cert);
242 return TRUE;
243 }
244 cons_show("Adding %s to trusted certificates.", cert->fingerprint);
245 tlscerts_add(cert);
246 tlscerts_free(cert);
247 return TRUE;
248 #else
249 cons_show("Manual certificate trust only supported when built with libmesode.");
250 return TRUE;
251 #endif
252 }
253
254 gboolean
cmd_tls_trusted(ProfWin * window,const char * const command,gchar ** args)255 cmd_tls_trusted(ProfWin* window, const char* const command, gchar** args)
256 {
257 #ifdef HAVE_LIBMESODE
258 GList* certs = tlscerts_list();
259 GList* curr = certs;
260
261 if (curr) {
262 cons_show("Trusted certificates:");
263 cons_show("");
264 } else {
265 cons_show("No trusted certificates found.");
266 }
267 while (curr) {
268 TLSCertificate* cert = curr->data;
269 cons_show_tlscert_summary(cert);
270 cons_show("");
271 curr = g_list_next(curr);
272 }
273 g_list_free_full(certs, (GDestroyNotify)tlscerts_free);
274 return TRUE;
275 #else
276 cons_show("Manual certificate trust only supported when built with libmesode.");
277 return TRUE;
278 #endif
279 }
280
281 gboolean
cmd_tls_revoke(ProfWin * window,const char * const command,gchar ** args)282 cmd_tls_revoke(ProfWin* window, const char* const command, gchar** args)
283 {
284 #ifdef HAVE_LIBMESODE
285 if (args[1] == NULL) {
286 cons_bad_cmd_usage(command);
287 } else {
288 gboolean res = tlscerts_revoke(args[1]);
289 if (res) {
290 cons_show("Trusted certificate revoked: %s", args[1]);
291 } else {
292 cons_show("Could not find certificate: %s", args[1]);
293 }
294 }
295 return TRUE;
296 #else
297 cons_show("Manual certificate trust only supported when built with libmesode.");
298 return TRUE;
299 #endif
300 }
301
302 gboolean
cmd_tls_cert(ProfWin * window,const char * const command,gchar ** args)303 cmd_tls_cert(ProfWin* window, const char* const command, gchar** args)
304 {
305 #ifdef HAVE_LIBMESODE
306 if (args[1]) {
307 TLSCertificate* cert = tlscerts_get_trusted(args[1]);
308 if (!cert) {
309 cons_show("No such certificate.");
310 } else {
311 cons_show_tlscert(cert);
312 tlscerts_free(cert);
313 }
314 return TRUE;
315 } else {
316 jabber_conn_status_t conn_status = connection_get_status();
317 if (conn_status != JABBER_CONNECTED) {
318 cons_show("You are not currently connected.");
319 return TRUE;
320 }
321 if (!connection_is_secured()) {
322 cons_show("No TLS connection established");
323 return TRUE;
324 }
325 TLSCertificate* cert = connection_get_tls_peer_cert();
326 if (!cert) {
327 cons_show("Error getting TLS certificate.");
328 return TRUE;
329 }
330 cons_show_tlscert(cert);
331 cons_show("");
332 tlscerts_free(cert);
333 return TRUE;
334 }
335 #else
336 cons_show("Certificate fetching not supported.");
337 return TRUE;
338 #endif
339 }
340
341 gboolean
cmd_connect(ProfWin * window,const char * const command,gchar ** args)342 cmd_connect(ProfWin* window, const char* const command, gchar** args)
343 {
344 jabber_conn_status_t conn_status = connection_get_status();
345 if (conn_status != JABBER_DISCONNECTED) {
346 cons_show("You are either connected already, or a login is in process.");
347 return TRUE;
348 }
349
350 gchar* opt_keys[] = { "server", "port", "tls", "auth", NULL };
351 gboolean parsed;
352
353 GHashTable* options = parse_options(&args[args[0] ? 1 : 0], opt_keys, &parsed);
354 if (!parsed) {
355 cons_bad_cmd_usage(command);
356 cons_show("");
357 options_destroy(options);
358 return TRUE;
359 }
360
361 char* altdomain = g_hash_table_lookup(options, "server");
362
363 char* tls_policy = g_hash_table_lookup(options, "tls");
364 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)) {
365 cons_bad_cmd_usage(command);
366 cons_show("");
367 options_destroy(options);
368 return TRUE;
369 }
370
371 char* auth_policy = g_hash_table_lookup(options, "auth");
372 if (auth_policy && (g_strcmp0(auth_policy, "default") != 0) && (g_strcmp0(auth_policy, "legacy") != 0)) {
373 cons_bad_cmd_usage(command);
374 cons_show("");
375 options_destroy(options);
376 return TRUE;
377 }
378
379 int port = 0;
380 if (g_hash_table_contains(options, "port")) {
381 char* port_str = g_hash_table_lookup(options, "port");
382 char* err_msg = NULL;
383 gboolean res = strtoi_range(port_str, &port, 1, 65535, &err_msg);
384 if (!res) {
385 cons_show(err_msg);
386 cons_show("");
387 free(err_msg);
388 port = 0;
389 options_destroy(options);
390 return TRUE;
391 }
392 }
393
394 char* user = args[0];
395 char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
396 if (!user) {
397 if (def) {
398 user = def;
399 cons_show("Using default account %s.", user);
400 } else {
401 cons_show("No default account.");
402 options_destroy(options);
403 return TRUE;
404 }
405 }
406
407 char* jid;
408 user = strdup(user);
409 g_free(def);
410
411 // connect with account
412 ProfAccount* account = accounts_get_account(user);
413 if (account) {
414 // override account options with connect options
415 if (altdomain != NULL)
416 account_set_server(account, altdomain);
417 if (port != 0)
418 account_set_port(account, port);
419 if (tls_policy != NULL)
420 account_set_tls_policy(account, tls_policy);
421 if (auth_policy != NULL)
422 account_set_auth_policy(account, auth_policy);
423
424 // use password if set
425 if (account->password) {
426 conn_status = cl_ev_connect_account(account);
427
428 // use eval_password if set
429 } else if (account->eval_password) {
430 gboolean res = account_eval_password(account);
431 if (res) {
432 conn_status = cl_ev_connect_account(account);
433 free(account->password);
434 account->password = NULL;
435 } else {
436 cons_show("Error evaluating password, see logs for details.");
437 account_free(account);
438 free(user);
439 options_destroy(options);
440 return TRUE;
441 }
442
443 // no account password setting, prompt
444 } else {
445 account->password = ui_ask_password(false);
446 conn_status = cl_ev_connect_account(account);
447 free(account->password);
448 account->password = NULL;
449 }
450
451 jid = account_create_connect_jid(account);
452 account_free(account);
453
454 // connect with JID
455 } else {
456 jid = g_utf8_strdown(user, -1);
457 char* passwd = ui_ask_password(false);
458 conn_status = cl_ev_connect_jid(jid, passwd, altdomain, port, tls_policy, auth_policy);
459 free(passwd);
460 }
461
462 if (conn_status == JABBER_DISCONNECTED) {
463 cons_show_error("Connection attempt for %s failed.", jid);
464 log_info("Connection attempt for %s failed", jid);
465 }
466
467 options_destroy(options);
468 free(jid);
469 free(user);
470
471 return TRUE;
472 }
473
474 gboolean
cmd_account_list(ProfWin * window,const char * const command,gchar ** args)475 cmd_account_list(ProfWin* window, const char* const command, gchar** args)
476 {
477 gchar** accounts = accounts_get_list();
478 cons_show_account_list(accounts);
479 g_strfreev(accounts);
480
481 return TRUE;
482 }
483
484 gboolean
cmd_account_show(ProfWin * window,const char * const command,gchar ** args)485 cmd_account_show(ProfWin* window, const char* const command, gchar** args)
486 {
487 char* account_name = args[1];
488 if (account_name == NULL) {
489 cons_bad_cmd_usage(command);
490 return TRUE;
491 }
492
493 ProfAccount* account = accounts_get_account(account_name);
494 if (account == NULL) {
495 cons_show("No such account.");
496 cons_show("");
497 } else {
498 cons_show_account(account);
499 account_free(account);
500 }
501
502 return TRUE;
503 }
504
505 gboolean
cmd_account_add(ProfWin * window,const char * const command,gchar ** args)506 cmd_account_add(ProfWin* window, const char* const command, gchar** args)
507 {
508 char* account_name = args[1];
509 if (account_name == NULL) {
510 cons_bad_cmd_usage(command);
511 return TRUE;
512 }
513
514 accounts_add(account_name, NULL, 0, NULL, NULL);
515 cons_show("Account created.");
516 cons_show("");
517
518 return TRUE;
519 }
520
521 gboolean
cmd_account_remove(ProfWin * window,const char * const command,gchar ** args)522 cmd_account_remove(ProfWin* window, const char* const command, gchar** args)
523 {
524 char* account_name = args[1];
525 if (!account_name) {
526 cons_bad_cmd_usage(command);
527 return TRUE;
528 }
529
530 char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
531 if (accounts_remove(account_name)) {
532 cons_show("Account %s removed.", account_name);
533 if (def && strcmp(def, account_name) == 0) {
534 prefs_set_string(PREF_DEFAULT_ACCOUNT, NULL);
535 cons_show("Default account removed because the corresponding account was removed.");
536 }
537 } else {
538 cons_show("Failed to remove account %s.", account_name);
539 cons_show("Either the account does not exist, or an unknown error occurred.");
540 }
541 cons_show("");
542 g_free(def);
543
544 return TRUE;
545 }
546
547 gboolean
cmd_account_enable(ProfWin * window,const char * const command,gchar ** args)548 cmd_account_enable(ProfWin* window, const char* const command, gchar** args)
549 {
550 char* account_name = args[1];
551 if (account_name == NULL) {
552 cons_bad_cmd_usage(command);
553 return TRUE;
554 }
555
556 if (accounts_enable(account_name)) {
557 cons_show("Account enabled.");
558 cons_show("");
559 } else {
560 cons_show("No such account: %s", account_name);
561 cons_show("");
562 }
563
564 return TRUE;
565 }
566 gboolean
cmd_account_disable(ProfWin * window,const char * const command,gchar ** args)567 cmd_account_disable(ProfWin* window, const char* const command, gchar** args)
568 {
569 char* account_name = args[1];
570 if (account_name == NULL) {
571 cons_bad_cmd_usage(command);
572 return TRUE;
573 }
574
575 if (accounts_disable(account_name)) {
576 cons_show("Account disabled.");
577 cons_show("");
578 } else {
579 cons_show("No such account: %s", account_name);
580 cons_show("");
581 }
582
583 return TRUE;
584 }
585
586 gboolean
cmd_account_rename(ProfWin * window,const char * const command,gchar ** args)587 cmd_account_rename(ProfWin* window, const char* const command, gchar** args)
588 {
589 if (g_strv_length(args) != 3) {
590 cons_bad_cmd_usage(command);
591 return TRUE;
592 }
593
594 char* account_name = args[1];
595 char* new_name = args[2];
596
597 if (accounts_rename(account_name, new_name)) {
598 cons_show("Account renamed.");
599 cons_show("");
600 } else {
601 cons_show("Either account %s doesn't exist, or account %s already exists.", account_name, new_name);
602 cons_show("");
603 }
604
605 return TRUE;
606 }
607
608 gboolean
cmd_account_default(ProfWin * window,const char * const command,gchar ** args)609 cmd_account_default(ProfWin* window, const char* const command, gchar** args)
610 {
611 if (g_strv_length(args) == 1) {
612 char* def = prefs_get_string(PREF_DEFAULT_ACCOUNT);
613 if (def) {
614 cons_show("The default account is %s.", def);
615 free(def);
616 } else {
617 cons_show("No default account.");
618 }
619 } else if (g_strv_length(args) == 2) {
620 if (strcmp(args[1], "off") == 0) {
621 prefs_set_string(PREF_DEFAULT_ACCOUNT, NULL);
622 cons_show("Removed default account.");
623 } else {
624 cons_bad_cmd_usage(command);
625 }
626 } else if (g_strv_length(args) == 3) {
627 if (strcmp(args[1], "set") == 0) {
628 ProfAccount* account_p = accounts_get_account(args[2]);
629 if (account_p) {
630 prefs_set_string(PREF_DEFAULT_ACCOUNT, args[2]);
631 cons_show("Default account set to %s.", args[2]);
632 account_free(account_p);
633 } else {
634 cons_show("Account %s does not exist.", args[2]);
635 }
636 } else {
637 cons_bad_cmd_usage(command);
638 }
639 } else {
640 cons_bad_cmd_usage(command);
641 }
642
643 return TRUE;
644 }
645
646 gboolean
_account_set_jid(char * account_name,char * jid)647 _account_set_jid(char* account_name, char* jid)
648 {
649 Jid* jidp = jid_create(jid);
650 if (jidp == NULL) {
651 cons_show("Malformed jid: %s", jid);
652 } else {
653 accounts_set_jid(account_name, jidp->barejid);
654 cons_show("Updated jid for account %s: %s", account_name, jidp->barejid);
655 if (jidp->resourcepart) {
656 accounts_set_resource(account_name, jidp->resourcepart);
657 cons_show("Updated resource for account %s: %s", account_name, jidp->resourcepart);
658 }
659 cons_show("");
660 }
661 jid_destroy(jidp);
662
663 return TRUE;
664 }
665
666 gboolean
_account_set_server(char * account_name,char * server)667 _account_set_server(char* account_name, char* server)
668 {
669 accounts_set_server(account_name, server);
670 cons_show("Updated server for account %s: %s", account_name, server);
671 cons_show("");
672 return TRUE;
673 }
674
675 gboolean
_account_set_port(char * account_name,char * port)676 _account_set_port(char* account_name, char* port)
677 {
678 int porti;
679 char* err_msg = NULL;
680 gboolean res = strtoi_range(port, &porti, 1, 65535, &err_msg);
681 if (!res) {
682 cons_show(err_msg);
683 cons_show("");
684 free(err_msg);
685 } else {
686 accounts_set_port(account_name, porti);
687 cons_show("Updated port for account %s: %s", account_name, port);
688 cons_show("");
689 }
690 return TRUE;
691 }
692
693 gboolean
_account_set_resource(char * account_name,char * resource)694 _account_set_resource(char* account_name, char* resource)
695 {
696 accounts_set_resource(account_name, resource);
697 if (connection_get_status() == JABBER_CONNECTED) {
698 cons_show("Updated resource for account %s: %s, reconnect to pick up the change.", account_name, resource);
699 } else {
700 cons_show("Updated resource for account %s: %s", account_name, resource);
701 }
702 cons_show("");
703 return TRUE;
704 }
705
706 gboolean
_account_set_password(char * account_name,char * password)707 _account_set_password(char* account_name, char* password)
708 {
709 ProfAccount* account = accounts_get_account(account_name);
710 if (account->eval_password) {
711 cons_show("Cannot set password when eval_password is set.");
712 } else {
713 accounts_set_password(account_name, password);
714 cons_show("Updated password for account %s", account_name);
715 cons_show("");
716 }
717 account_free(account);
718 return TRUE;
719 }
720
721 gboolean
_account_set_eval_password(char * account_name,char * eval_password)722 _account_set_eval_password(char* account_name, char* eval_password)
723 {
724 ProfAccount* account = accounts_get_account(account_name);
725 if (account->password) {
726 cons_show("Cannot set eval_password when password is set.");
727 } else {
728 accounts_set_eval_password(account_name, eval_password);
729 cons_show("Updated eval_password for account %s", account_name);
730 cons_show("");
731 }
732 account_free(account);
733 return TRUE;
734 }
735
736 gboolean
_account_set_muc(char * account_name,char * muc)737 _account_set_muc(char* account_name, char* muc)
738 {
739 accounts_set_muc_service(account_name, muc);
740 cons_show("Updated muc service for account %s: %s", account_name, muc);
741 cons_show("");
742 return TRUE;
743 }
744
745 gboolean
_account_set_nick(char * account_name,char * nick)746 _account_set_nick(char* account_name, char* nick)
747 {
748 accounts_set_muc_nick(account_name, nick);
749 cons_show("Updated muc nick for account %s: %s", account_name, nick);
750 cons_show("");
751 return TRUE;
752 }
753
754 gboolean
_account_set_otr(char * account_name,char * policy)755 _account_set_otr(char* account_name, char* policy)
756 {
757 if ((g_strcmp0(policy, "manual") != 0)
758 && (g_strcmp0(policy, "opportunistic") != 0)
759 && (g_strcmp0(policy, "always") != 0)) {
760 cons_show("OTR policy must be one of: manual, opportunistic or always.");
761 } else {
762 accounts_set_otr_policy(account_name, policy);
763 cons_show("Updated OTR policy for account %s: %s", account_name, policy);
764 cons_show("");
765 }
766 return TRUE;
767 }
768
769 gboolean
_account_set_status(char * account_name,char * status)770 _account_set_status(char* account_name, char* status)
771 {
772 if (!valid_resource_presence_string(status) && (strcmp(status, "last") != 0)) {
773 cons_show("Invalid status: %s", status);
774 } else {
775 accounts_set_login_presence(account_name, status);
776 cons_show("Updated login status for account %s: %s", account_name, status);
777 }
778 cons_show("");
779 return TRUE;
780 }
781
782 gboolean
_account_set_pgpkeyid(char * account_name,char * pgpkeyid)783 _account_set_pgpkeyid(char* account_name, char* pgpkeyid)
784 {
785 #ifdef HAVE_LIBGPGME
786 char* err_str = NULL;
787 if (!p_gpg_valid_key(pgpkeyid, &err_str)) {
788 cons_show("Invalid PGP key ID specified: %s, see /pgp keys", err_str);
789 } else {
790 accounts_set_pgp_keyid(account_name, pgpkeyid);
791 cons_show("Updated PGP key ID for account %s: %s", account_name, pgpkeyid);
792 }
793 free(err_str);
794 #else
795 cons_show("PGP support is not included in this build.");
796 #endif
797 cons_show("");
798 return TRUE;
799 }
800
801 gboolean
_account_set_startscript(char * account_name,char * script)802 _account_set_startscript(char* account_name, char* script)
803 {
804 accounts_set_script_start(account_name, script);
805 cons_show("Updated start script for account %s: %s", account_name, script);
806 return TRUE;
807 }
808
809 gboolean
_account_set_theme(char * account_name,char * theme)810 _account_set_theme(char* account_name, char* theme)
811 {
812 if (!theme_exists(theme)) {
813 cons_show("Theme does not exist: %s", theme);
814 return TRUE;
815 }
816
817 accounts_set_theme(account_name, theme);
818 if (connection_get_status() == JABBER_CONNECTED) {
819 ProfAccount* account = accounts_get_account(session_get_account_name());
820 if (account) {
821 if (g_strcmp0(account->name, account_name) == 0) {
822 theme_load(theme, false);
823 ui_load_colours();
824 if (prefs_get_boolean(PREF_ROSTER)) {
825 ui_show_roster();
826 } else {
827 ui_hide_roster();
828 }
829 if (prefs_get_boolean(PREF_OCCUPANTS)) {
830 ui_show_all_room_rosters();
831 } else {
832 ui_hide_all_room_rosters();
833 }
834 ui_redraw();
835 }
836 account_free(account);
837 }
838 }
839 cons_show("Updated theme for account %s: %s", account_name, theme);
840 return TRUE;
841 }
842
843 gboolean
_account_set_tls(char * account_name,char * policy)844 _account_set_tls(char* account_name, char* policy)
845 {
846 if ((g_strcmp0(policy, "force") != 0)
847 && (g_strcmp0(policy, "allow") != 0)
848 && (g_strcmp0(policy, "trust") != 0)
849 && (g_strcmp0(policy, "disable") != 0)
850 && (g_strcmp0(policy, "legacy") != 0)) {
851 cons_show("TLS policy must be one of: force, allow, legacy or disable.");
852 } else {
853 accounts_set_tls_policy(account_name, policy);
854 cons_show("Updated TLS policy for account %s: %s", account_name, policy);
855 cons_show("");
856 }
857 return TRUE;
858 }
859
860 gboolean
_account_set_auth(char * account_name,char * policy)861 _account_set_auth(char* account_name, char* policy)
862 {
863 if ((g_strcmp0(policy, "default") != 0)
864 && (g_strcmp0(policy, "legacy") != 0)) {
865 cons_show("Auth policy must be either default or legacy.");
866 } else {
867 accounts_set_auth_policy(account_name, policy);
868 cons_show("Updated auth policy for account %s: %s", account_name, policy);
869 cons_show("");
870 }
871 return TRUE;
872 }
873
874 gboolean
_account_set_presence_priority(char * account_name,char * presence,char * priority)875 _account_set_presence_priority(char* account_name, char* presence, char* priority)
876 {
877 int intval;
878 char* err_msg = NULL;
879 gboolean res = strtoi_range(priority, &intval, -128, 127, &err_msg);
880 if (!res) {
881 cons_show(err_msg);
882 free(err_msg);
883 return TRUE;
884 }
885
886 resource_presence_t presence_type = resource_presence_from_string(presence);
887 switch (presence_type) {
888 case (RESOURCE_ONLINE):
889 accounts_set_priority_online(account_name, intval);
890 break;
891 case (RESOURCE_CHAT):
892 accounts_set_priority_chat(account_name, intval);
893 break;
894 case (RESOURCE_AWAY):
895 accounts_set_priority_away(account_name, intval);
896 break;
897 case (RESOURCE_XA):
898 accounts_set_priority_xa(account_name, intval);
899 break;
900 case (RESOURCE_DND):
901 accounts_set_priority_dnd(account_name, intval);
902 break;
903 }
904
905 jabber_conn_status_t conn_status = connection_get_status();
906 if (conn_status == JABBER_CONNECTED) {
907 char* connected_account = session_get_account_name();
908 resource_presence_t last_presence = accounts_get_last_presence(connected_account);
909 if (presence_type == last_presence) {
910 cl_ev_presence_send(last_presence, 0);
911 }
912 }
913 cons_show("Updated %s priority for account %s: %s", presence, account_name, priority);
914 cons_show("");
915 return TRUE;
916 }
917
918 gboolean
cmd_account_set(ProfWin * window,const char * const command,gchar ** args)919 cmd_account_set(ProfWin* window, const char* const command, gchar** args)
920 {
921 if (g_strv_length(args) != 4) {
922 cons_bad_cmd_usage(command);
923 return TRUE;
924 }
925
926 char* account_name = args[1];
927 if (!accounts_account_exists(account_name)) {
928 cons_show("Account %s doesn't exist", account_name);
929 cons_show("");
930 return TRUE;
931 }
932
933 char* property = args[2];
934 char* value = args[3];
935 if (strcmp(property, "jid") == 0)
936 return _account_set_jid(account_name, value);
937 if (strcmp(property, "server") == 0)
938 return _account_set_server(account_name, value);
939 if (strcmp(property, "port") == 0)
940 return _account_set_port(account_name, value);
941 if (strcmp(property, "resource") == 0)
942 return _account_set_resource(account_name, value);
943 if (strcmp(property, "password") == 0)
944 return _account_set_password(account_name, value);
945 if (strcmp(property, "eval_password") == 0)
946 return _account_set_eval_password(account_name, value);
947 if (strcmp(property, "muc") == 0)
948 return _account_set_muc(account_name, value);
949 if (strcmp(property, "nick") == 0)
950 return _account_set_nick(account_name, value);
951 if (strcmp(property, "otr") == 0)
952 return _account_set_otr(account_name, value);
953 if (strcmp(property, "status") == 0)
954 return _account_set_status(account_name, value);
955 if (strcmp(property, "pgpkeyid") == 0)
956 return _account_set_pgpkeyid(account_name, value);
957 if (strcmp(property, "startscript") == 0)
958 return _account_set_startscript(account_name, value);
959 if (strcmp(property, "theme") == 0)
960 return _account_set_theme(account_name, value);
961 if (strcmp(property, "tls") == 0)
962 return _account_set_tls(account_name, value);
963 if (strcmp(property, "auth") == 0)
964 return _account_set_auth(account_name, value);
965
966 if (valid_resource_presence_string(property)) {
967 return _account_set_presence_priority(account_name, property, value);
968 }
969
970 cons_show("Invalid property: %s", property);
971 cons_show("");
972
973 return TRUE;
974 }
975
976 gboolean
cmd_account_clear(ProfWin * window,const char * const command,gchar ** args)977 cmd_account_clear(ProfWin* window, const char* const command, gchar** args)
978 {
979 if (g_strv_length(args) != 3) {
980 cons_bad_cmd_usage(command);
981 return TRUE;
982 }
983
984 char* account_name = args[1];
985 if (!accounts_account_exists(account_name)) {
986 cons_show("Account %s doesn't exist", account_name);
987 cons_show("");
988 return TRUE;
989 }
990
991 char* property = args[2];
992 if (strcmp(property, "password") == 0) {
993 accounts_clear_password(account_name);
994 cons_show("Removed password for account %s", account_name);
995 cons_show("");
996 } else if (strcmp(property, "eval_password") == 0) {
997 accounts_clear_eval_password(account_name);
998 cons_show("Removed eval password for account %s", account_name);
999 cons_show("");
1000 } else if (strcmp(property, "server") == 0) {
1001 accounts_clear_server(account_name);
1002 cons_show("Removed server for account %s", account_name);
1003 cons_show("");
1004 } else if (strcmp(property, "port") == 0) {
1005 accounts_clear_port(account_name);
1006 cons_show("Removed port for account %s", account_name);
1007 cons_show("");
1008 } else if (strcmp(property, "otr") == 0) {
1009 accounts_clear_otr(account_name);
1010 cons_show("OTR policy removed for account %s", account_name);
1011 cons_show("");
1012 } else if (strcmp(property, "pgpkeyid") == 0) {
1013 accounts_clear_pgp_keyid(account_name);
1014 cons_show("Removed PGP key ID for account %s", account_name);
1015 cons_show("");
1016 } else if (strcmp(property, "startscript") == 0) {
1017 accounts_clear_script_start(account_name);
1018 cons_show("Removed start script for account %s", account_name);
1019 cons_show("");
1020 } else if (strcmp(property, "theme") == 0) {
1021 accounts_clear_theme(account_name);
1022 cons_show("Removed theme for account %s", account_name);
1023 cons_show("");
1024 } else if (strcmp(property, "muc") == 0) {
1025 accounts_clear_muc(account_name);
1026 cons_show("Removed MUC service for account %s", account_name);
1027 cons_show("");
1028 } else if (strcmp(property, "resource") == 0) {
1029 accounts_clear_resource(account_name);
1030 cons_show("Removed resource for account %s", account_name);
1031 cons_show("");
1032 } else {
1033 cons_show("Invalid property: %s", property);
1034 cons_show("");
1035 }
1036
1037 return TRUE;
1038 }
1039
1040 gboolean
cmd_account(ProfWin * window,const char * const command,gchar ** args)1041 cmd_account(ProfWin* window, const char* const command, gchar** args)
1042 {
1043 if (args[0] != NULL) {
1044 cons_bad_cmd_usage(command);
1045 cons_show("");
1046 return TRUE;
1047 }
1048
1049 if (connection_get_status() != JABBER_CONNECTED) {
1050 cons_bad_cmd_usage(command);
1051 return TRUE;
1052 }
1053
1054 ProfAccount* account = accounts_get_account(session_get_account_name());
1055 if (account) {
1056 cons_show_account(account);
1057 account_free(account);
1058 } else {
1059 log_error("Could not get accounts");
1060 }
1061
1062 return TRUE;
1063 }
1064
1065 gboolean
cmd_script(ProfWin * window,const char * const command,gchar ** args)1066 cmd_script(ProfWin* window, const char* const command, gchar** args)
1067 {
1068 if ((g_strcmp0(args[0], "run") == 0) && args[1]) {
1069 gboolean res = scripts_exec(args[1]);
1070 if (!res) {
1071 cons_show("Could not find script %s", args[1]);
1072 }
1073 } else if (g_strcmp0(args[0], "list") == 0) {
1074 GSList* scripts = scripts_list();
1075 cons_show_scripts(scripts);
1076 g_slist_free_full(scripts, g_free);
1077 } else if ((g_strcmp0(args[0], "show") == 0) && args[1]) {
1078 GSList* commands = scripts_read(args[1]);
1079 cons_show_script(args[1], commands);
1080 g_slist_free_full(commands, g_free);
1081 } else {
1082 cons_bad_cmd_usage(command);
1083 }
1084
1085 return TRUE;
1086 }
1087
1088 /* escape a string into csv and write it to the file descriptor */
1089 static int
_writecsv(int fd,const char * const str)1090 _writecsv(int fd, const char* const str)
1091 {
1092 if (!str)
1093 return 0;
1094 size_t len = strlen(str);
1095 char* s = malloc(2 * len * sizeof(char));
1096 char* c = s;
1097 for (int i = 0; i < strlen(str); i++) {
1098 if (str[i] != '"')
1099 *c++ = str[i];
1100 else {
1101 *c++ = '"';
1102 *c++ = '"';
1103 len++;
1104 }
1105 }
1106 if (-1 == write(fd, s, len)) {
1107 cons_show("error: failed to write '%s' to the requested file: %s", s, strerror(errno));
1108 return -1;
1109 }
1110 free(s);
1111 return 0;
1112 }
1113
1114 gboolean
cmd_export(ProfWin * window,const char * const command,gchar ** args)1115 cmd_export(ProfWin* window, const char* const command, gchar** args)
1116 {
1117 jabber_conn_status_t conn_status = connection_get_status();
1118
1119 if (conn_status != JABBER_CONNECTED) {
1120 cons_show("You are not currently connected.");
1121 cons_show("");
1122 return TRUE;
1123 } else {
1124 int fd;
1125 GSList* list = NULL;
1126 char* path = get_expanded_path(args[0]);
1127
1128 fd = open(path, O_WRONLY | O_CREAT, 00600);
1129
1130 if (-1 == fd) {
1131 cons_show("error: cannot open %s: %s", args[0], strerror(errno));
1132 cons_show("");
1133 return TRUE;
1134 }
1135
1136 if (-1 == write(fd, "jid,name\n", strlen("jid,name\n")))
1137 goto write_error;
1138
1139 list = roster_get_contacts(ROSTER_ORD_NAME);
1140 if (list) {
1141 GSList* curr = list;
1142 while (curr) {
1143 PContact contact = curr->data;
1144 const char* jid = p_contact_barejid(contact);
1145 const char* name = p_contact_name(contact);
1146
1147 /* write the data to the file */
1148 if (-1 == write(fd, "\"", 1))
1149 goto write_error;
1150 if (-1 == _writecsv(fd, jid))
1151 goto write_error;
1152 if (-1 == write(fd, "\",\"", 3))
1153 goto write_error;
1154 if (-1 == _writecsv(fd, name))
1155 goto write_error;
1156 if (-1 == write(fd, "\"\n", 2))
1157 goto write_error;
1158
1159 /* loop */
1160 curr = g_slist_next(curr);
1161 }
1162 cons_show("Contacts exported successfully");
1163 cons_show("");
1164 } else {
1165 cons_show("No contacts in roster.");
1166 cons_show("");
1167 }
1168
1169 g_slist_free(list);
1170 close(fd);
1171 return TRUE;
1172 write_error:
1173 cons_show("error: write failed: %s", strerror(errno));
1174 cons_show("");
1175 g_slist_free(list);
1176 close(fd);
1177 return TRUE;
1178 }
1179 }
1180
1181 gboolean
cmd_sub(ProfWin * window,const char * const command,gchar ** args)1182 cmd_sub(ProfWin* window, const char* const command, gchar** args)
1183 {
1184 jabber_conn_status_t conn_status = connection_get_status();
1185
1186 if (conn_status != JABBER_CONNECTED) {
1187 cons_show("You are currently not connected.");
1188 return TRUE;
1189 }
1190
1191 char *subcmd, *jid;
1192 subcmd = args[0];
1193 jid = args[1];
1194
1195 if (subcmd == NULL) {
1196 cons_bad_cmd_usage(command);
1197 return TRUE;
1198 }
1199
1200 if (strcmp(subcmd, "sent") == 0) {
1201 cons_show_sent_subs();
1202 return TRUE;
1203 }
1204
1205 if (strcmp(subcmd, "received") == 0) {
1206 cons_show_received_subs();
1207 return TRUE;
1208 }
1209
1210 if ((window->type != WIN_CHAT) && (jid == NULL)) {
1211 cons_show("You must specify a contact.");
1212 return TRUE;
1213 }
1214
1215 if (jid == NULL) {
1216 ProfChatWin* chatwin = (ProfChatWin*)window;
1217 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
1218 jid = chatwin->barejid;
1219 }
1220
1221 Jid* jidp = jid_create(jid);
1222
1223 if (strcmp(subcmd, "allow") == 0) {
1224 presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBED);
1225 cons_show("Accepted subscription for %s", jidp->barejid);
1226 log_info("Accepted subscription for %s", jidp->barejid);
1227 } else if (strcmp(subcmd, "deny") == 0) {
1228 presence_subscription(jidp->barejid, PRESENCE_UNSUBSCRIBED);
1229 cons_show("Deleted/denied subscription for %s", jidp->barejid);
1230 log_info("Deleted/denied subscription for %s", jidp->barejid);
1231 } else if (strcmp(subcmd, "request") == 0) {
1232 presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBE);
1233 cons_show("Sent subscription request to %s.", jidp->barejid);
1234 log_info("Sent subscription request to %s.", jidp->barejid);
1235 } else if (strcmp(subcmd, "show") == 0) {
1236 PContact contact = roster_get_contact(jidp->barejid);
1237 if ((contact == NULL) || (p_contact_subscription(contact) == NULL)) {
1238 if (window->type == WIN_CHAT) {
1239 win_println(window, THEME_DEFAULT, "-", "No subscription information for %s.", jidp->barejid);
1240 } else {
1241 cons_show("No subscription information for %s.", jidp->barejid);
1242 }
1243 } else {
1244 if (window->type == WIN_CHAT) {
1245 if (p_contact_pending_out(contact)) {
1246 win_println(window, THEME_DEFAULT, "-", "%s subscription status: %s, request pending.",
1247 jidp->barejid, p_contact_subscription(contact));
1248 } else {
1249 win_println(window, THEME_DEFAULT, "-", "%s subscription status: %s.", jidp->barejid,
1250 p_contact_subscription(contact));
1251 }
1252 } else {
1253 if (p_contact_pending_out(contact)) {
1254 cons_show("%s subscription status: %s, request pending.",
1255 jidp->barejid, p_contact_subscription(contact));
1256 } else {
1257 cons_show("%s subscription status: %s.", jidp->barejid,
1258 p_contact_subscription(contact));
1259 }
1260 }
1261 }
1262 } else {
1263 cons_bad_cmd_usage(command);
1264 }
1265
1266 jid_destroy(jidp);
1267
1268 return TRUE;
1269 }
1270
1271 gboolean
cmd_disconnect(ProfWin * window,const char * const command,gchar ** args)1272 cmd_disconnect(ProfWin* window, const char* const command, gchar** args)
1273 {
1274 if (connection_get_status() != JABBER_CONNECTED) {
1275 cons_show("You are not currently connected.");
1276 return TRUE;
1277 }
1278
1279 cl_ev_disconnect();
1280
1281 ui_redraw();
1282
1283 return TRUE;
1284 }
1285
1286 gboolean
cmd_quit(ProfWin * window,const char * const command,gchar ** args)1287 cmd_quit(ProfWin* window, const char* const command, gchar** args)
1288 {
1289 log_info("Profanity is shutting down...");
1290 exit(0);
1291 return FALSE;
1292 }
1293
1294 gboolean
cmd_wins_unread(ProfWin * window,const char * const command,gchar ** args)1295 cmd_wins_unread(ProfWin* window, const char* const command, gchar** args)
1296 {
1297 cons_show_wins(TRUE);
1298 return TRUE;
1299 }
1300
1301 gboolean
cmd_wins_attention(ProfWin * window,const char * const command,gchar ** args)1302 cmd_wins_attention(ProfWin* window, const char* const command, gchar** args)
1303 {
1304 cons_show_wins_attention();
1305 return TRUE;
1306 }
1307
1308 gboolean
cmd_wins_prune(ProfWin * window,const char * const command,gchar ** args)1309 cmd_wins_prune(ProfWin* window, const char* const command, gchar** args)
1310 {
1311 ui_prune_wins();
1312 return TRUE;
1313 }
1314
1315 gboolean
cmd_wins_swap(ProfWin * window,const char * const command,gchar ** args)1316 cmd_wins_swap(ProfWin* window, const char* const command, gchar** args)
1317 {
1318 if ((args[1] == NULL) || (args[2] == NULL)) {
1319 cons_bad_cmd_usage(command);
1320 return TRUE;
1321 }
1322
1323 int source_win = atoi(args[1]);
1324 int target_win = atoi(args[2]);
1325
1326 if ((source_win == 1) || (target_win == 1)) {
1327 cons_show("Cannot move console window.");
1328 return TRUE;
1329 }
1330
1331 if (source_win == 10 || target_win == 10) {
1332 cons_show("Window 10 does not exist");
1333 return TRUE;
1334 }
1335
1336 if (source_win == target_win) {
1337 cons_show("Same source and target window supplied.");
1338 return TRUE;
1339 }
1340
1341 if (wins_get_by_num(source_win) == NULL) {
1342 cons_show("Window %d does not exist", source_win);
1343 return TRUE;
1344 }
1345
1346 if (wins_get_by_num(target_win) == NULL) {
1347 cons_show("Window %d does not exist", target_win);
1348 return TRUE;
1349 }
1350
1351 wins_swap(source_win, target_win);
1352 cons_show("Swapped windows %d <-> %d", source_win, target_win);
1353 return TRUE;
1354 }
1355
1356 gboolean
cmd_wins(ProfWin * window,const char * const command,gchar ** args)1357 cmd_wins(ProfWin* window, const char* const command, gchar** args)
1358 {
1359 if (args[0] != NULL) {
1360 cons_bad_cmd_usage(command);
1361 return TRUE;
1362 }
1363
1364 cons_show_wins(FALSE);
1365 return TRUE;
1366 }
1367
1368 gboolean
cmd_close(ProfWin * window,const char * const command,gchar ** args)1369 cmd_close(ProfWin* window, const char* const command, gchar** args)
1370 {
1371 jabber_conn_status_t conn_status = connection_get_status();
1372
1373 if (g_strcmp0(args[0], "all") == 0) {
1374 int count = ui_close_all_wins();
1375 if (count == 0) {
1376 cons_show("No windows to close.");
1377 } else if (count == 1) {
1378 cons_show("Closed 1 window.");
1379 } else {
1380 cons_show("Closed %d windows.", count);
1381 }
1382 rosterwin_roster();
1383 return TRUE;
1384 }
1385
1386 if (g_strcmp0(args[0], "read") == 0) {
1387 int count = ui_close_read_wins();
1388 if (count == 0) {
1389 cons_show("No windows to close.");
1390 } else if (count == 1) {
1391 cons_show("Closed 1 window.");
1392 } else {
1393 cons_show("Closed %d windows.", count);
1394 }
1395 rosterwin_roster();
1396 return TRUE;
1397 }
1398
1399 gboolean is_num = TRUE;
1400 int index = 0;
1401 if (args[0] != NULL) {
1402 for (int i = 0; i < strlen(args[0]); i++) {
1403 if (!isdigit((int)args[0][i])) {
1404 is_num = FALSE;
1405 break;
1406 }
1407 }
1408
1409 if (is_num) {
1410 index = atoi(args[0]);
1411 }
1412 } else {
1413 index = wins_get_current_num();
1414 }
1415
1416 if (is_num) {
1417 if (index < 0 || index == 10) {
1418 cons_show("No such window exists.");
1419 return TRUE;
1420 }
1421
1422 if (index == 1) {
1423 cons_show("Cannot close console window.");
1424 return TRUE;
1425 }
1426
1427 ProfWin* toclose = wins_get_by_num(index);
1428 if (!toclose) {
1429 cons_show("Window is not open.");
1430 return TRUE;
1431 }
1432
1433 // check for unsaved form
1434 if (ui_win_has_unsaved_form(index)) {
1435 win_println(window, THEME_DEFAULT, "-", "You have unsaved changes, use /form submit or /form cancel");
1436 return TRUE;
1437 }
1438
1439 // handle leaving rooms, or chat
1440 if (conn_status == JABBER_CONNECTED) {
1441 ui_close_connected_win(index);
1442 }
1443
1444 // close the window
1445 ui_close_win(index);
1446 cons_show("Closed window %d", index);
1447 wins_tidy();
1448
1449 rosterwin_roster();
1450 return TRUE;
1451 } else {
1452 if (g_strcmp0(args[0], "console") == 0) {
1453 cons_show("Cannot close console window.");
1454 return TRUE;
1455 }
1456
1457 ProfWin* toclose = wins_get_by_string(args[0]);
1458 if (!toclose) {
1459 cons_show("Window \"%s\" does not exist.", args[0]);
1460 return TRUE;
1461 }
1462 index = wins_get_num(toclose);
1463
1464 // check for unsaved form
1465 if (ui_win_has_unsaved_form(index)) {
1466 win_println(window, THEME_DEFAULT, "-", "You have unsaved changes, use /form submit or /form cancel");
1467 return TRUE;
1468 }
1469
1470 // handle leaving rooms, or chat
1471 if (conn_status == JABBER_CONNECTED) {
1472 ui_close_connected_win(index);
1473 }
1474
1475 // close the window
1476 ui_close_win(index);
1477 cons_show("Closed window %s", args[0]);
1478 wins_tidy();
1479
1480 rosterwin_roster();
1481 return TRUE;
1482 }
1483 }
1484
1485 gboolean
cmd_win(ProfWin * window,const char * const command,gchar ** args)1486 cmd_win(ProfWin* window, const char* const command, gchar** args)
1487 {
1488 gboolean is_num = TRUE;
1489 for (int i = 0; i < strlen(args[0]); i++) {
1490 if (!isdigit((int)args[0][i])) {
1491 is_num = FALSE;
1492 break;
1493 }
1494 }
1495
1496 if (is_num) {
1497 int num = atoi(args[0]);
1498
1499 ProfWin* focuswin = wins_get_by_num(num);
1500 if (!focuswin) {
1501 cons_show("Window %d does not exist.", num);
1502 } else {
1503 ui_focus_win(focuswin);
1504 }
1505 } else {
1506 ProfWin* focuswin = wins_get_by_string(args[0]);
1507 if (!focuswin) {
1508 cons_show("Window \"%s\" does not exist.", args[0]);
1509 } else {
1510 ui_focus_win(focuswin);
1511 }
1512 }
1513
1514 return TRUE;
1515 }
1516
1517 static void
_cmd_list_commands(GList * commands)1518 _cmd_list_commands(GList* commands)
1519 {
1520 int maxlen = 0;
1521 GList* curr = commands;
1522 while (curr) {
1523 gchar* cmd = curr->data;
1524 int len = strlen(cmd);
1525 if (len > maxlen)
1526 maxlen = len;
1527 curr = g_list_next(curr);
1528 }
1529
1530 GString* cmds = g_string_new("");
1531 curr = commands;
1532 int count = 0;
1533 while (curr) {
1534 gchar* cmd = curr->data;
1535 if (count == 5) {
1536 cons_show(cmds->str);
1537 g_string_free(cmds, TRUE);
1538 cmds = g_string_new("");
1539 count = 0;
1540 }
1541 g_string_append_printf(cmds, "%-*s", maxlen + 1, cmd);
1542 curr = g_list_next(curr);
1543 count++;
1544 }
1545 cons_show(cmds->str);
1546 g_string_free(cmds, TRUE);
1547 g_list_free(curr);
1548
1549 cons_show("");
1550 cons_show("Use /help [command] without the leading slash, for help on a specific command");
1551 cons_show("");
1552 }
1553
1554 static void
_cmd_help_cmd_list(const char * const tag)1555 _cmd_help_cmd_list(const char* const tag)
1556 {
1557 cons_show("");
1558 ProfWin* console = wins_get_console();
1559 if (tag) {
1560 win_println(console, THEME_HELP_HEADER, "-", "%s commands", tag);
1561 } else {
1562 win_println(console, THEME_HELP_HEADER, "-", "All commands");
1563 }
1564
1565 GList* ordered_commands = NULL;
1566
1567 if (g_strcmp0(tag, "plugins") == 0) {
1568 GList* plugins_cmds = plugins_get_command_names();
1569 GList* curr = plugins_cmds;
1570 while (curr) {
1571 ordered_commands = g_list_insert_sorted(ordered_commands, curr->data, (GCompareFunc)g_strcmp0);
1572 curr = g_list_next(curr);
1573 }
1574 g_list_free(plugins_cmds);
1575 } else {
1576 ordered_commands = cmd_get_ordered(tag);
1577
1578 // add plugins if showing all commands
1579 if (!tag) {
1580 GList* plugins_cmds = plugins_get_command_names();
1581 GList* curr = plugins_cmds;
1582 while (curr) {
1583 ordered_commands = g_list_insert_sorted(ordered_commands, curr->data, (GCompareFunc)g_strcmp0);
1584 curr = g_list_next(curr);
1585 }
1586 g_list_free(plugins_cmds);
1587 }
1588 }
1589
1590 _cmd_list_commands(ordered_commands);
1591 g_list_free(ordered_commands);
1592 }
1593
1594 gboolean
cmd_help(ProfWin * window,const char * const command,gchar ** args)1595 cmd_help(ProfWin* window, const char* const command, gchar** args)
1596 {
1597 int num_args = g_strv_length(args);
1598 if (num_args == 0) {
1599 cons_help();
1600 } else if (strcmp(args[0], "search_all") == 0) {
1601 if (args[1] == NULL) {
1602 cons_bad_cmd_usage(command);
1603 } else {
1604 GList* cmds = cmd_search_index_all(args[1]);
1605 if (cmds == NULL) {
1606 cons_show("No commands found.");
1607 } else {
1608 GList* curr = cmds;
1609 GList* results = NULL;
1610 while (curr) {
1611 results = g_list_insert_sorted(results, curr->data, (GCompareFunc)g_strcmp0);
1612 curr = g_list_next(curr);
1613 }
1614 cons_show("Search results:");
1615 _cmd_list_commands(results);
1616 g_list_free(results);
1617 }
1618 g_list_free(cmds);
1619 }
1620 } else if (strcmp(args[0], "search_any") == 0) {
1621 if (args[1] == NULL) {
1622 cons_bad_cmd_usage(command);
1623 } else {
1624 GList* cmds = cmd_search_index_any(args[1]);
1625 if (cmds == NULL) {
1626 cons_show("No commands found.");
1627 } else {
1628 GList* curr = cmds;
1629 GList* results = NULL;
1630 while (curr) {
1631 results = g_list_insert_sorted(results, curr->data, (GCompareFunc)g_strcmp0);
1632 curr = g_list_next(curr);
1633 }
1634 cons_show("Search results:");
1635 _cmd_list_commands(results);
1636 g_list_free(results);
1637 }
1638 g_list_free(cmds);
1639 }
1640 } else if (strcmp(args[0], "commands") == 0) {
1641 if (args[1]) {
1642 if (!cmd_valid_tag(args[1])) {
1643 cons_bad_cmd_usage(command);
1644 } else {
1645 _cmd_help_cmd_list(args[1]);
1646 }
1647 } else {
1648 _cmd_help_cmd_list(NULL);
1649 }
1650 } else if (strcmp(args[0], "navigation") == 0) {
1651 cons_navigation_help();
1652 } else {
1653 char* cmd = args[0];
1654 char *cmd_with_slash = g_strdup_printf("/%s", cmd);
1655
1656 Command* command = cmd_get(cmd_with_slash);
1657 if (command) {
1658 cons_show_help(cmd_with_slash, &command->help);
1659 } else {
1660 CommandHelp* commandHelp = plugins_get_help(cmd_with_slash);
1661 if (commandHelp) {
1662 cons_show_help(cmd_with_slash, commandHelp);
1663 } else {
1664 cons_show("No such command.");
1665 }
1666 }
1667 cons_show("");
1668 g_free(cmd_with_slash);
1669 }
1670
1671 return TRUE;
1672 }
1673
1674 gboolean
cmd_about(ProfWin * window,const char * const command,gchar ** args)1675 cmd_about(ProfWin* window, const char* const command, gchar** args)
1676 {
1677 cons_show("");
1678 cons_about();
1679 return TRUE;
1680 }
1681
1682 gboolean
cmd_prefs(ProfWin * window,const char * const command,gchar ** args)1683 cmd_prefs(ProfWin* window, const char* const command, gchar** args)
1684 {
1685 if (args[0] == NULL) {
1686 cons_prefs();
1687 cons_show("Use the /account command for preferences for individual accounts.");
1688 } else if (strcmp(args[0], "ui") == 0) {
1689 cons_show("");
1690 cons_show_ui_prefs();
1691 cons_show("");
1692 } else if (strcmp(args[0], "desktop") == 0) {
1693 cons_show("");
1694 cons_show_desktop_prefs();
1695 cons_show("");
1696 } else if (strcmp(args[0], "chat") == 0) {
1697 cons_show("");
1698 cons_show_chat_prefs();
1699 cons_show("");
1700 } else if (strcmp(args[0], "log") == 0) {
1701 cons_show("");
1702 cons_show_log_prefs();
1703 cons_show("");
1704 } else if (strcmp(args[0], "conn") == 0) {
1705 cons_show("");
1706 cons_show_connection_prefs();
1707 cons_show("");
1708 } else if (strcmp(args[0], "presence") == 0) {
1709 cons_show("");
1710 cons_show_presence_prefs();
1711 cons_show("");
1712 } else if (strcmp(args[0], "otr") == 0) {
1713 cons_show("");
1714 cons_show_otr_prefs();
1715 cons_show("");
1716 } else if (strcmp(args[0], "pgp") == 0) {
1717 cons_show("");
1718 cons_show_pgp_prefs();
1719 cons_show("");
1720 } else if (strcmp(args[0], "omemo") == 0) {
1721 cons_show("");
1722 cons_show_omemo_prefs();
1723 cons_show("");
1724 } else {
1725 cons_bad_cmd_usage(command);
1726 }
1727
1728 return TRUE;
1729 }
1730
1731 gboolean
cmd_theme(ProfWin * window,const char * const command,gchar ** args)1732 cmd_theme(ProfWin* window, const char* const command, gchar** args)
1733 {
1734 // 'full-load' means to load the theme including the settings (not just [colours])
1735 gboolean fullload = (g_strcmp0(args[0], "full-load") == 0);
1736
1737 // list themes
1738 if (g_strcmp0(args[0], "list") == 0) {
1739 GSList* themes = theme_list();
1740 cons_show_themes(themes);
1741 g_slist_free_full(themes, g_free);
1742
1743 // load a theme
1744 } else if (g_strcmp0(args[0], "load") == 0 || fullload) {
1745 if (args[1] == NULL) {
1746 cons_bad_cmd_usage(command);
1747 } else if (theme_load(args[1], fullload)) {
1748 ui_load_colours();
1749 prefs_set_string(PREF_THEME, args[1]);
1750 if (prefs_get_boolean(PREF_ROSTER)) {
1751 ui_show_roster();
1752 } else {
1753 ui_hide_roster();
1754 }
1755 if (prefs_get_boolean(PREF_OCCUPANTS)) {
1756 ui_show_all_room_rosters();
1757 } else {
1758 ui_hide_all_room_rosters();
1759 }
1760 ui_resize();
1761 cons_show("Loaded theme: %s", args[1]);
1762 } else {
1763 cons_show("Couldn't find theme: %s", args[1]);
1764 }
1765
1766 // show colours
1767 } else if (g_strcmp0(args[0], "colours") == 0) {
1768 cons_theme_colours();
1769 } else if (g_strcmp0(args[0], "properties") == 0) {
1770 cons_theme_properties();
1771 } else {
1772 cons_bad_cmd_usage(command);
1773 }
1774
1775 return TRUE;
1776 }
1777
1778 static void
_who_room(ProfWin * window,const char * const command,gchar ** args)1779 _who_room(ProfWin* window, const char* const command, gchar** args)
1780 {
1781 if ((g_strv_length(args) == 2) && args[1]) {
1782 cons_show("Argument group is not applicable to chat rooms.");
1783 return;
1784 }
1785
1786 // bad arg
1787 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)) {
1788 cons_bad_cmd_usage(command);
1789 return;
1790 }
1791
1792 ProfMucWin* mucwin = (ProfMucWin*)window;
1793 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
1794
1795 // presence filter
1796 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)) {
1797
1798 char* presence = args[0];
1799 GList* occupants = muc_roster(mucwin->roomjid);
1800
1801 // no arg, show all contacts
1802 if ((presence == NULL) || (g_strcmp0(presence, "any") == 0)) {
1803 mucwin_roster(mucwin, occupants, NULL);
1804
1805 // available
1806 } else if (strcmp("available", presence) == 0) {
1807 GList* filtered = NULL;
1808
1809 while (occupants) {
1810 Occupant* occupant = occupants->data;
1811 if (muc_occupant_available(occupant)) {
1812 filtered = g_list_append(filtered, occupant);
1813 }
1814 occupants = g_list_next(occupants);
1815 }
1816
1817 mucwin_roster(mucwin, filtered, "available");
1818
1819 // unavailable
1820 } else if (strcmp("unavailable", presence) == 0) {
1821 GList* filtered = NULL;
1822
1823 while (occupants) {
1824 Occupant* occupant = occupants->data;
1825 if (!muc_occupant_available(occupant)) {
1826 filtered = g_list_append(filtered, occupant);
1827 }
1828 occupants = g_list_next(occupants);
1829 }
1830
1831 mucwin_roster(mucwin, filtered, "unavailable");
1832
1833 // show specific status
1834 } else {
1835 GList* filtered = NULL;
1836
1837 while (occupants) {
1838 Occupant* occupant = occupants->data;
1839 const char* presence_str = string_from_resource_presence(occupant->presence);
1840 if (strcmp(presence_str, presence) == 0) {
1841 filtered = g_list_append(filtered, occupant);
1842 }
1843 occupants = g_list_next(occupants);
1844 }
1845
1846 mucwin_roster(mucwin, filtered, presence);
1847 }
1848
1849 g_list_free(occupants);
1850
1851 // role or affiliation filter
1852 } else {
1853 if (g_strcmp0(args[0], "moderator") == 0) {
1854 mucwin_show_role_list(mucwin, MUC_ROLE_MODERATOR);
1855 return;
1856 }
1857 if (g_strcmp0(args[0], "participant") == 0) {
1858 mucwin_show_role_list(mucwin, MUC_ROLE_PARTICIPANT);
1859 return;
1860 }
1861 if (g_strcmp0(args[0], "visitor") == 0) {
1862 mucwin_show_role_list(mucwin, MUC_ROLE_VISITOR);
1863 return;
1864 }
1865
1866 if (g_strcmp0(args[0], "owner") == 0) {
1867 mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_OWNER);
1868 return;
1869 }
1870 if (g_strcmp0(args[0], "admin") == 0) {
1871 mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_ADMIN);
1872 return;
1873 }
1874 if (g_strcmp0(args[0], "member") == 0) {
1875 mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_MEMBER);
1876 return;
1877 }
1878 if (g_strcmp0(args[0], "outcast") == 0) {
1879 mucwin_show_affiliation_list(mucwin, MUC_AFFILIATION_OUTCAST);
1880 return;
1881 }
1882 }
1883 }
1884
1885 static void
_who_roster(ProfWin * window,const char * const command,gchar ** args)1886 _who_roster(ProfWin* window, const char* const command, gchar** args)
1887 {
1888 char* presence = args[0];
1889
1890 // bad arg
1891 if (presence
1892 && (strcmp(presence, "online") != 0)
1893 && (strcmp(presence, "available") != 0)
1894 && (strcmp(presence, "unavailable") != 0)
1895 && (strcmp(presence, "offline") != 0)
1896 && (strcmp(presence, "away") != 0)
1897 && (strcmp(presence, "chat") != 0)
1898 && (strcmp(presence, "xa") != 0)
1899 && (strcmp(presence, "dnd") != 0)
1900 && (strcmp(presence, "any") != 0)) {
1901 cons_bad_cmd_usage(command);
1902 return;
1903 }
1904
1905 char* group = NULL;
1906 if ((g_strv_length(args) == 2) && args[1]) {
1907 group = args[1];
1908 }
1909
1910 cons_show("");
1911 GSList* list = NULL;
1912 if (group) {
1913 list = roster_get_group(group, ROSTER_ORD_NAME);
1914 if (list == NULL) {
1915 cons_show("No such group: %s.", group);
1916 return;
1917 }
1918 } else {
1919 list = roster_get_contacts(ROSTER_ORD_NAME);
1920 if (list == NULL) {
1921 cons_show("No contacts in roster.");
1922 return;
1923 }
1924 }
1925
1926 // no arg, show all contacts
1927 if ((presence == NULL) || (g_strcmp0(presence, "any") == 0)) {
1928 if (group) {
1929 if (list == NULL) {
1930 cons_show("No contacts in group %s.", group);
1931 } else {
1932 cons_show("%s:", group);
1933 cons_show_contacts(list);
1934 }
1935 } else {
1936 if (list == NULL) {
1937 cons_show("You have no contacts.");
1938 } else {
1939 cons_show("All contacts:");
1940 cons_show_contacts(list);
1941 }
1942 }
1943
1944 // available
1945 } else if (strcmp("available", presence) == 0) {
1946 GSList* filtered = NULL;
1947
1948 GSList* curr = list;
1949 while (curr) {
1950 PContact contact = curr->data;
1951 if (p_contact_is_available(contact)) {
1952 filtered = g_slist_append(filtered, contact);
1953 }
1954 curr = g_slist_next(curr);
1955 }
1956
1957 if (group) {
1958 if (filtered == NULL) {
1959 cons_show("No contacts in group %s are %s.", group, presence);
1960 } else {
1961 cons_show("%s (%s):", group, presence);
1962 cons_show_contacts(filtered);
1963 }
1964 } else {
1965 if (filtered == NULL) {
1966 cons_show("No contacts are %s.", presence);
1967 } else {
1968 cons_show("Contacts (%s):", presence);
1969 cons_show_contacts(filtered);
1970 }
1971 }
1972 g_slist_free(filtered);
1973
1974 // unavailable
1975 } else if (strcmp("unavailable", presence) == 0) {
1976 GSList* filtered = NULL;
1977
1978 GSList* curr = list;
1979 while (curr) {
1980 PContact contact = curr->data;
1981 if (!p_contact_is_available(contact)) {
1982 filtered = g_slist_append(filtered, contact);
1983 }
1984 curr = g_slist_next(curr);
1985 }
1986
1987 if (group) {
1988 if (filtered == NULL) {
1989 cons_show("No contacts in group %s are %s.", group, presence);
1990 } else {
1991 cons_show("%s (%s):", group, presence);
1992 cons_show_contacts(filtered);
1993 }
1994 } else {
1995 if (filtered == NULL) {
1996 cons_show("No contacts are %s.", presence);
1997 } else {
1998 cons_show("Contacts (%s):", presence);
1999 cons_show_contacts(filtered);
2000 }
2001 }
2002 g_slist_free(filtered);
2003
2004 // online, available resources
2005 } else if (strcmp("online", presence) == 0) {
2006 GSList* filtered = NULL;
2007
2008 GSList* curr = list;
2009 while (curr) {
2010 PContact contact = curr->data;
2011 if (p_contact_has_available_resource(contact)) {
2012 filtered = g_slist_append(filtered, contact);
2013 }
2014 curr = g_slist_next(curr);
2015 }
2016
2017 if (group) {
2018 if (filtered == NULL) {
2019 cons_show("No contacts in group %s are %s.", group, presence);
2020 } else {
2021 cons_show("%s (%s):", group, presence);
2022 cons_show_contacts(filtered);
2023 }
2024 } else {
2025 if (filtered == NULL) {
2026 cons_show("No contacts are %s.", presence);
2027 } else {
2028 cons_show("Contacts (%s):", presence);
2029 cons_show_contacts(filtered);
2030 }
2031 }
2032 g_slist_free(filtered);
2033
2034 // offline, no available resources
2035 } else if (strcmp("offline", presence) == 0) {
2036 GSList* filtered = NULL;
2037
2038 GSList* curr = list;
2039 while (curr) {
2040 PContact contact = curr->data;
2041 if (!p_contact_has_available_resource(contact)) {
2042 filtered = g_slist_append(filtered, contact);
2043 }
2044 curr = g_slist_next(curr);
2045 }
2046
2047 if (group) {
2048 if (filtered == NULL) {
2049 cons_show("No contacts in group %s are %s.", group, presence);
2050 } else {
2051 cons_show("%s (%s):", group, presence);
2052 cons_show_contacts(filtered);
2053 }
2054 } else {
2055 if (filtered == NULL) {
2056 cons_show("No contacts are %s.", presence);
2057 } else {
2058 cons_show("Contacts (%s):", presence);
2059 cons_show_contacts(filtered);
2060 }
2061 }
2062 g_slist_free(filtered);
2063
2064 // show specific status
2065 } else {
2066 GSList* filtered = NULL;
2067
2068 GSList* curr = list;
2069 while (curr) {
2070 PContact contact = curr->data;
2071 if (strcmp(p_contact_presence(contact), presence) == 0) {
2072 filtered = g_slist_append(filtered, contact);
2073 }
2074 curr = g_slist_next(curr);
2075 }
2076
2077 if (group) {
2078 if (filtered == NULL) {
2079 cons_show("No contacts in group %s are %s.", group, presence);
2080 } else {
2081 cons_show("%s (%s):", group, presence);
2082 cons_show_contacts(filtered);
2083 }
2084 } else {
2085 if (filtered == NULL) {
2086 cons_show("No contacts are %s.", presence);
2087 } else {
2088 cons_show("Contacts (%s):", presence);
2089 cons_show_contacts(filtered);
2090 }
2091 }
2092 g_slist_free(filtered);
2093 }
2094
2095 g_slist_free(list);
2096 }
2097
2098 gboolean
cmd_who(ProfWin * window,const char * const command,gchar ** args)2099 cmd_who(ProfWin* window, const char* const command, gchar** args)
2100 {
2101 jabber_conn_status_t conn_status = connection_get_status();
2102
2103 if (conn_status != JABBER_CONNECTED) {
2104 cons_show("You are not currently connected.");
2105 } else if (window->type == WIN_MUC) {
2106 _who_room(window, command, args);
2107 } else {
2108 _who_roster(window, command, args);
2109 }
2110
2111 if (window->type != WIN_CONSOLE && window->type != WIN_MUC) {
2112 status_bar_new(1, WIN_CONSOLE, "console");
2113 }
2114
2115 return TRUE;
2116 }
2117
2118 static void
_cmd_msg_chatwin(const char * const barejid,const char * const msg)2119 _cmd_msg_chatwin(const char* const barejid, const char* const msg)
2120 {
2121 ProfChatWin* chatwin = wins_get_chat(barejid);
2122 if (!chatwin) {
2123 // NOTE: This will also start the new OMEMO session and send a MAM request.
2124 chatwin = chatwin_new(barejid);
2125 }
2126 ui_focus_win((ProfWin*)chatwin);
2127
2128 if (msg) {
2129 // NOTE: In case the message is OMEMO encrypted, we can't be sure
2130 // whether the key bundles of the recipient have already been
2131 // received. In the case that *no* bundles have been received yet,
2132 // the message won't be sent, and an error is shown to the user.
2133 // Other cases are not handled here.
2134 cl_ev_send_msg(chatwin, msg, NULL);
2135 } else {
2136 #ifdef HAVE_LIBOTR
2137 // Start the OTR session after this (i.e. the first) message was sent
2138 if (otr_is_secure(barejid)) {
2139 chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
2140 }
2141 #endif // HAVE_LIBOTR
2142 }
2143 }
2144
2145 gboolean
cmd_msg(ProfWin * window,const char * const command,gchar ** args)2146 cmd_msg(ProfWin* window, const char* const command, gchar** args)
2147 {
2148 char* usr = args[0];
2149 char* msg = args[1];
2150
2151 jabber_conn_status_t conn_status = connection_get_status();
2152
2153 if (conn_status != JABBER_CONNECTED) {
2154 cons_show("You are not currently connected.");
2155 return TRUE;
2156 }
2157
2158 // send private message when in MUC room
2159 if (window->type == WIN_MUC) {
2160 ProfMucWin* mucwin = (ProfMucWin*)window;
2161 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
2162
2163 Occupant* occupant = muc_roster_item(mucwin->roomjid, usr);
2164 if (occupant) {
2165 // in case of non-anon muc send regular chatmessage
2166 if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS) {
2167 Jid* jidp = jid_create(occupant->jid);
2168
2169 _cmd_msg_chatwin(jidp->barejid, msg);
2170 win_println(window, THEME_DEFAULT, "-", "Starting direct message with occupant \"%s\" from room \"%s\" as \"%s\".", usr, mucwin->roomjid, jidp->barejid);
2171 cons_show("Starting direct message with occupant \"%s\" from room \"%s\" as \"%s\".", usr, mucwin->roomjid, jidp->barejid);
2172
2173 jid_destroy(jidp);
2174 } else {
2175 // otherwise send mucpm
2176 GString* full_jid = g_string_new(mucwin->roomjid);
2177 g_string_append(full_jid, "/");
2178 g_string_append(full_jid, usr);
2179
2180 ProfPrivateWin* privwin = wins_get_private(full_jid->str);
2181 if (!privwin) {
2182 privwin = (ProfPrivateWin*)wins_new_private(full_jid->str);
2183 }
2184 ui_focus_win((ProfWin*)privwin);
2185
2186 if (msg) {
2187 cl_ev_send_priv_msg(privwin, msg, NULL);
2188 }
2189
2190 g_string_free(full_jid, TRUE);
2191 }
2192
2193 } else {
2194 win_println(window, THEME_DEFAULT, "-", "No such participant \"%s\" in room.", usr);
2195 }
2196
2197 return TRUE;
2198
2199 // send chat message
2200 } else {
2201 char* barejid = roster_barejid_from_name(usr);
2202 if (barejid == NULL) {
2203 barejid = usr;
2204 }
2205
2206 _cmd_msg_chatwin(barejid, msg);
2207
2208 return TRUE;
2209 }
2210 }
2211
2212 gboolean
cmd_group(ProfWin * window,const char * const command,gchar ** args)2213 cmd_group(ProfWin* window, const char* const command, gchar** args)
2214 {
2215 jabber_conn_status_t conn_status = connection_get_status();
2216
2217 if (conn_status != JABBER_CONNECTED) {
2218 cons_show("You are not currently connected.");
2219 return TRUE;
2220 }
2221
2222 // list all groups
2223 if (args[1] == NULL) {
2224 GList* groups = roster_get_groups();
2225 GList* curr = groups;
2226 if (curr) {
2227 cons_show("Groups:");
2228 while (curr) {
2229 cons_show(" %s", curr->data);
2230 curr = g_list_next(curr);
2231 }
2232
2233 g_list_free_full(groups, g_free);
2234 } else {
2235 cons_show("No groups.");
2236 }
2237 return TRUE;
2238 }
2239
2240 // show contacts in group
2241 if (strcmp(args[1], "show") == 0) {
2242 char* group = args[2];
2243 if (group == NULL) {
2244 cons_bad_cmd_usage(command);
2245 return TRUE;
2246 }
2247
2248 GSList* list = roster_get_group(group, ROSTER_ORD_NAME);
2249 cons_show_roster_group(group, list);
2250 return TRUE;
2251 }
2252
2253 // add contact to group
2254 if (strcmp(args[1], "add") == 0) {
2255 char* group = args[2];
2256 char* contact = args[3];
2257
2258 if ((group == NULL) || (contact == NULL)) {
2259 cons_bad_cmd_usage(command);
2260 return TRUE;
2261 }
2262
2263 char* barejid = roster_barejid_from_name(contact);
2264 if (barejid == NULL) {
2265 barejid = contact;
2266 }
2267
2268 PContact pcontact = roster_get_contact(barejid);
2269 if (pcontact == NULL) {
2270 cons_show("Contact not found in roster: %s", barejid);
2271 return TRUE;
2272 }
2273
2274 if (p_contact_in_group(pcontact, group)) {
2275 const char* display_name = p_contact_name_or_jid(pcontact);
2276 ui_contact_already_in_group(display_name, group);
2277 } else {
2278 roster_send_add_to_group(group, pcontact);
2279 }
2280
2281 return TRUE;
2282 }
2283
2284 // remove contact from group
2285 if (strcmp(args[1], "remove") == 0) {
2286 char* group = args[2];
2287 char* contact = args[3];
2288
2289 if ((group == NULL) || (contact == NULL)) {
2290 cons_bad_cmd_usage(command);
2291 return TRUE;
2292 }
2293
2294 char* barejid = roster_barejid_from_name(contact);
2295 if (barejid == NULL) {
2296 barejid = contact;
2297 }
2298
2299 PContact pcontact = roster_get_contact(barejid);
2300 if (pcontact == NULL) {
2301 cons_show("Contact not found in roster: %s", barejid);
2302 return TRUE;
2303 }
2304
2305 if (!p_contact_in_group(pcontact, group)) {
2306 const char* display_name = p_contact_name_or_jid(pcontact);
2307 ui_contact_not_in_group(display_name, group);
2308 } else {
2309 roster_send_remove_from_group(group, pcontact);
2310 }
2311
2312 return TRUE;
2313 }
2314
2315 cons_bad_cmd_usage(command);
2316 return TRUE;
2317 }
2318
2319 gboolean
cmd_roster(ProfWin * window,const char * const command,gchar ** args)2320 cmd_roster(ProfWin* window, const char* const command, gchar** args)
2321 {
2322 jabber_conn_status_t conn_status = connection_get_status();
2323
2324 // show roster
2325 if (args[0] == NULL) {
2326 if (conn_status != JABBER_CONNECTED) {
2327 cons_show("You are not currently connected.");
2328 return TRUE;
2329 }
2330
2331 GSList* list = roster_get_contacts(ROSTER_ORD_NAME);
2332 cons_show_roster(list);
2333 g_slist_free(list);
2334 return TRUE;
2335
2336 // show roster, only online contacts
2337 } else if (g_strcmp0(args[0], "online") == 0) {
2338 if (conn_status != JABBER_CONNECTED) {
2339 cons_show("You are not currently connected.");
2340 return TRUE;
2341 }
2342
2343 GSList* list = roster_get_contacts_online();
2344 cons_show_roster(list);
2345 g_slist_free(list);
2346 return TRUE;
2347
2348 // set roster size
2349 } else if (g_strcmp0(args[0], "size") == 0) {
2350 if (!args[1]) {
2351 cons_bad_cmd_usage(command);
2352 return TRUE;
2353 }
2354 int intval = 0;
2355 char* err_msg = NULL;
2356 gboolean res = strtoi_range(args[1], &intval, 1, 99, &err_msg);
2357 if (res) {
2358 prefs_set_roster_size(intval);
2359 cons_show("Roster screen size set to: %d%%", intval);
2360 if (conn_status == JABBER_CONNECTED && prefs_get_boolean(PREF_ROSTER)) {
2361 wins_resize_all();
2362 }
2363 return TRUE;
2364 } else {
2365 cons_show(err_msg);
2366 free(err_msg);
2367 return TRUE;
2368 }
2369
2370 // set line wrapping
2371 } else if (g_strcmp0(args[0], "wrap") == 0) {
2372 if (!args[1]) {
2373 cons_bad_cmd_usage(command);
2374 return TRUE;
2375 } else {
2376 _cmd_set_boolean_preference(args[1], command, "Roster panel line wrap", PREF_ROSTER_WRAP);
2377 rosterwin_roster();
2378 return TRUE;
2379 }
2380
2381 // header settings
2382 } else if (g_strcmp0(args[0], "header") == 0) {
2383 if (g_strcmp0(args[1], "char") == 0) {
2384 if (!args[2]) {
2385 cons_bad_cmd_usage(command);
2386 } else if (g_strcmp0(args[2], "none") == 0) {
2387 prefs_clear_roster_header_char();
2388 cons_show("Roster header char removed.");
2389 rosterwin_roster();
2390 } else {
2391 prefs_set_roster_header_char(args[2][0]);
2392 cons_show("Roster header char set to %c.", args[2][0]);
2393 rosterwin_roster();
2394 }
2395 } else {
2396 cons_bad_cmd_usage(command);
2397 }
2398 return TRUE;
2399
2400 // contact settings
2401 } else if (g_strcmp0(args[0], "contact") == 0) {
2402 if (g_strcmp0(args[1], "char") == 0) {
2403 if (!args[2]) {
2404 cons_bad_cmd_usage(command);
2405 } else if (g_strcmp0(args[2], "none") == 0) {
2406 prefs_clear_roster_contact_char();
2407 cons_show("Roster contact char removed.");
2408 rosterwin_roster();
2409 } else {
2410 prefs_set_roster_contact_char(args[2][0]);
2411 cons_show("Roster contact char set to %c.", args[2][0]);
2412 rosterwin_roster();
2413 }
2414 } else if (g_strcmp0(args[1], "indent") == 0) {
2415 if (!args[2]) {
2416 cons_bad_cmd_usage(command);
2417 } else {
2418 int intval = 0;
2419 char* err_msg = NULL;
2420 gboolean res = strtoi_range(args[2], &intval, 0, 10, &err_msg);
2421 if (res) {
2422 prefs_set_roster_contact_indent(intval);
2423 cons_show("Roster contact indent set to: %d", intval);
2424 rosterwin_roster();
2425 } else {
2426 cons_show(err_msg);
2427 free(err_msg);
2428 }
2429 }
2430 } else {
2431 cons_bad_cmd_usage(command);
2432 }
2433 return TRUE;
2434
2435 // resource settings
2436 } else if (g_strcmp0(args[0], "resource") == 0) {
2437 if (g_strcmp0(args[1], "char") == 0) {
2438 if (!args[2]) {
2439 cons_bad_cmd_usage(command);
2440 } else if (g_strcmp0(args[2], "none") == 0) {
2441 prefs_clear_roster_resource_char();
2442 cons_show("Roster resource char removed.");
2443 rosterwin_roster();
2444 } else {
2445 prefs_set_roster_resource_char(args[2][0]);
2446 cons_show("Roster resource char set to %c.", args[2][0]);
2447 rosterwin_roster();
2448 }
2449 } else if (g_strcmp0(args[1], "indent") == 0) {
2450 if (!args[2]) {
2451 cons_bad_cmd_usage(command);
2452 } else {
2453 int intval = 0;
2454 char* err_msg = NULL;
2455 gboolean res = strtoi_range(args[2], &intval, 0, 10, &err_msg);
2456 if (res) {
2457 prefs_set_roster_resource_indent(intval);
2458 cons_show("Roster resource indent set to: %d", intval);
2459 rosterwin_roster();
2460 } else {
2461 cons_show(err_msg);
2462 free(err_msg);
2463 }
2464 }
2465 } else if (g_strcmp0(args[1], "join") == 0) {
2466 _cmd_set_boolean_preference(args[2], command, "Roster join", PREF_ROSTER_RESOURCE_JOIN);
2467 rosterwin_roster();
2468 return TRUE;
2469 } else {
2470 cons_bad_cmd_usage(command);
2471 }
2472 return TRUE;
2473
2474 // presence settings
2475 } else if (g_strcmp0(args[0], "presence") == 0) {
2476 if (g_strcmp0(args[1], "indent") == 0) {
2477 if (!args[2]) {
2478 cons_bad_cmd_usage(command);
2479 } else {
2480 int intval = 0;
2481 char* err_msg = NULL;
2482 gboolean res = strtoi_range(args[2], &intval, -1, 10, &err_msg);
2483 if (res) {
2484 prefs_set_roster_presence_indent(intval);
2485 cons_show("Roster presence indent set to: %d", intval);
2486 rosterwin_roster();
2487 } else {
2488 cons_show(err_msg);
2489 free(err_msg);
2490 }
2491 }
2492 } else {
2493 cons_bad_cmd_usage(command);
2494 }
2495 return TRUE;
2496
2497 // show/hide roster
2498 } else if ((g_strcmp0(args[0], "show") == 0) || (g_strcmp0(args[0], "hide") == 0)) {
2499 preference_t pref;
2500 const char* pref_str;
2501 if (args[1] == NULL) {
2502 pref = PREF_ROSTER;
2503 pref_str = "";
2504 } else if (g_strcmp0(args[1], "offline") == 0) {
2505 pref = PREF_ROSTER_OFFLINE;
2506 pref_str = "offline";
2507 } else if (g_strcmp0(args[1], "resource") == 0) {
2508 pref = PREF_ROSTER_RESOURCE;
2509 pref_str = "resource";
2510 } else if (g_strcmp0(args[1], "presence") == 0) {
2511 pref = PREF_ROSTER_PRESENCE;
2512 pref_str = "presence";
2513 } else if (g_strcmp0(args[1], "status") == 0) {
2514 pref = PREF_ROSTER_STATUS;
2515 pref_str = "status";
2516 } else if (g_strcmp0(args[1], "empty") == 0) {
2517 pref = PREF_ROSTER_EMPTY;
2518 pref_str = "empty";
2519 } else if (g_strcmp0(args[1], "priority") == 0) {
2520 pref = PREF_ROSTER_PRIORITY;
2521 pref_str = "priority";
2522 } else if (g_strcmp0(args[1], "contacts") == 0) {
2523 pref = PREF_ROSTER_CONTACTS;
2524 pref_str = "contacts";
2525 } else if (g_strcmp0(args[1], "rooms") == 0) {
2526 pref = PREF_ROSTER_ROOMS;
2527 pref_str = "rooms";
2528 } else if (g_strcmp0(args[1], "unsubscribed") == 0) {
2529 pref = PREF_ROSTER_UNSUBSCRIBED;
2530 pref_str = "unsubscribed";
2531 } else {
2532 cons_bad_cmd_usage(command);
2533 return TRUE;
2534 }
2535
2536 gboolean val;
2537 if (g_strcmp0(args[0], "show") == 0) {
2538 val = TRUE;
2539 } else { // "hide"
2540 val = FALSE;
2541 }
2542
2543 cons_show("Roster%s%s %s (was %s)", strlen(pref_str) == 0 ? "" : " ", pref_str,
2544 val == TRUE ? "enabled" : "disabled",
2545 prefs_get_boolean(pref) == TRUE ? "enabled" : "disabled");
2546 prefs_set_boolean(pref, val);
2547 if (conn_status == JABBER_CONNECTED) {
2548 if (pref == PREF_ROSTER) {
2549 if (val == TRUE) {
2550 ui_show_roster();
2551 } else {
2552 ui_hide_roster();
2553 }
2554 } else {
2555 rosterwin_roster();
2556 }
2557 }
2558 return TRUE;
2559
2560 // roster grouping
2561 } else if (g_strcmp0(args[0], "by") == 0) {
2562 if (g_strcmp0(args[1], "group") == 0) {
2563 cons_show("Grouping roster by roster group");
2564 prefs_set_string(PREF_ROSTER_BY, "group");
2565 if (conn_status == JABBER_CONNECTED) {
2566 rosterwin_roster();
2567 }
2568 return TRUE;
2569 } else if (g_strcmp0(args[1], "presence") == 0) {
2570 cons_show("Grouping roster by presence");
2571 prefs_set_string(PREF_ROSTER_BY, "presence");
2572 if (conn_status == JABBER_CONNECTED) {
2573 rosterwin_roster();
2574 }
2575 return TRUE;
2576 } else if (g_strcmp0(args[1], "none") == 0) {
2577 cons_show("Roster grouping disabled");
2578 prefs_set_string(PREF_ROSTER_BY, "none");
2579 if (conn_status == JABBER_CONNECTED) {
2580 rosterwin_roster();
2581 }
2582 return TRUE;
2583 } else {
2584 cons_bad_cmd_usage(command);
2585 return TRUE;
2586 }
2587
2588 // roster item order
2589 } else if (g_strcmp0(args[0], "order") == 0) {
2590 if (g_strcmp0(args[1], "name") == 0) {
2591 cons_show("Ordering roster by name");
2592 prefs_set_string(PREF_ROSTER_ORDER, "name");
2593 if (conn_status == JABBER_CONNECTED) {
2594 rosterwin_roster();
2595 }
2596 return TRUE;
2597 } else if (g_strcmp0(args[1], "presence") == 0) {
2598 cons_show("Ordering roster by presence");
2599 prefs_set_string(PREF_ROSTER_ORDER, "presence");
2600 if (conn_status == JABBER_CONNECTED) {
2601 rosterwin_roster();
2602 }
2603 return TRUE;
2604 } else {
2605 cons_bad_cmd_usage(command);
2606 return TRUE;
2607 }
2608
2609 } else if (g_strcmp0(args[0], "count") == 0) {
2610 if (g_strcmp0(args[1], "zero") == 0) {
2611 _cmd_set_boolean_preference(args[2], command, "Roster header zero count", PREF_ROSTER_COUNT_ZERO);
2612 if (conn_status == JABBER_CONNECTED) {
2613 rosterwin_roster();
2614 }
2615 return TRUE;
2616 } else if (g_strcmp0(args[1], "unread") == 0) {
2617 cons_show("Roster header count set to unread");
2618 prefs_set_string(PREF_ROSTER_COUNT, "unread");
2619 if (conn_status == JABBER_CONNECTED) {
2620 rosterwin_roster();
2621 }
2622 return TRUE;
2623 } else if (g_strcmp0(args[1], "items") == 0) {
2624 cons_show("Roster header count set to items");
2625 prefs_set_string(PREF_ROSTER_COUNT, "items");
2626 if (conn_status == JABBER_CONNECTED) {
2627 rosterwin_roster();
2628 }
2629 return TRUE;
2630 } else if (g_strcmp0(args[1], "off") == 0) {
2631 cons_show("Disabling roster header count");
2632 prefs_set_string(PREF_ROSTER_COUNT, "off");
2633 if (conn_status == JABBER_CONNECTED) {
2634 rosterwin_roster();
2635 }
2636 return TRUE;
2637 } else {
2638 cons_bad_cmd_usage(command);
2639 return TRUE;
2640 }
2641
2642 } else if (g_strcmp0(args[0], "color") == 0) {
2643 _cmd_set_boolean_preference(args[1], command, "Roster consistent colors", PREF_ROSTER_COLOR_NICK);
2644 ui_show_roster();
2645 return TRUE;
2646
2647 } else if (g_strcmp0(args[0], "unread") == 0) {
2648 if (g_strcmp0(args[1], "before") == 0) {
2649 cons_show("Roster unread message count: before");
2650 prefs_set_string(PREF_ROSTER_UNREAD, "before");
2651 if (conn_status == JABBER_CONNECTED) {
2652 rosterwin_roster();
2653 }
2654 return TRUE;
2655 } else if (g_strcmp0(args[1], "after") == 0) {
2656 cons_show("Roster unread message count: after");
2657 prefs_set_string(PREF_ROSTER_UNREAD, "after");
2658 if (conn_status == JABBER_CONNECTED) {
2659 rosterwin_roster();
2660 }
2661 return TRUE;
2662 } else if (g_strcmp0(args[1], "off") == 0) {
2663 cons_show("Roster unread message count: off");
2664 prefs_set_string(PREF_ROSTER_UNREAD, "off");
2665 if (conn_status == JABBER_CONNECTED) {
2666 rosterwin_roster();
2667 }
2668 return TRUE;
2669 } else {
2670 cons_bad_cmd_usage(command);
2671 return TRUE;
2672 }
2673
2674 } else if (g_strcmp0(args[0], "private") == 0) {
2675 if (g_strcmp0(args[1], "char") == 0) {
2676 if (!args[2]) {
2677 cons_bad_cmd_usage(command);
2678 } else if (g_strcmp0(args[2], "none") == 0) {
2679 prefs_clear_roster_private_char();
2680 cons_show("Roster private room chat char removed.");
2681 rosterwin_roster();
2682 } else {
2683 prefs_set_roster_private_char(args[2][0]);
2684 cons_show("Roster private room chat char set to %c.", args[2][0]);
2685 rosterwin_roster();
2686 }
2687 return TRUE;
2688 } else if (g_strcmp0(args[1], "room") == 0) {
2689 cons_show("Showing room private chats under room.");
2690 prefs_set_string(PREF_ROSTER_PRIVATE, "room");
2691 if (conn_status == JABBER_CONNECTED) {
2692 rosterwin_roster();
2693 }
2694 return TRUE;
2695 } else if (g_strcmp0(args[1], "group") == 0) {
2696 cons_show("Showing room private chats as roster group.");
2697 prefs_set_string(PREF_ROSTER_PRIVATE, "group");
2698 if (conn_status == JABBER_CONNECTED) {
2699 rosterwin_roster();
2700 }
2701 return TRUE;
2702 } else if (g_strcmp0(args[1], "off") == 0) {
2703 cons_show("Hiding room private chats in roster.");
2704 prefs_set_string(PREF_ROSTER_PRIVATE, "off");
2705 if (conn_status == JABBER_CONNECTED) {
2706 rosterwin_roster();
2707 }
2708 return TRUE;
2709 } else {
2710 cons_bad_cmd_usage(command);
2711 return TRUE;
2712 }
2713
2714 } else if (g_strcmp0(args[0], "room") == 0) {
2715 if (g_strcmp0(args[1], "char") == 0) {
2716 if (!args[2]) {
2717 cons_bad_cmd_usage(command);
2718 } else if (g_strcmp0(args[2], "none") == 0) {
2719 prefs_clear_roster_room_char();
2720 cons_show("Roster room char removed.");
2721 rosterwin_roster();
2722 } else {
2723 prefs_set_roster_room_char(args[2][0]);
2724 cons_show("Roster room char set to %c.", args[2][0]);
2725 rosterwin_roster();
2726 }
2727 return TRUE;
2728 } else if (g_strcmp0(args[1], "position") == 0) {
2729 if (g_strcmp0(args[2], "first") == 0) {
2730 cons_show("Showing rooms first in roster.");
2731 prefs_set_string(PREF_ROSTER_ROOMS_POS, "first");
2732 if (conn_status == JABBER_CONNECTED) {
2733 rosterwin_roster();
2734 }
2735 return TRUE;
2736 } else if (g_strcmp0(args[2], "last") == 0) {
2737 cons_show("Showing rooms last in roster.");
2738 prefs_set_string(PREF_ROSTER_ROOMS_POS, "last");
2739 if (conn_status == JABBER_CONNECTED) {
2740 rosterwin_roster();
2741 }
2742 return TRUE;
2743 } else {
2744 cons_bad_cmd_usage(command);
2745 return TRUE;
2746 }
2747 } else if (g_strcmp0(args[1], "order") == 0) {
2748 if (g_strcmp0(args[2], "name") == 0) {
2749 cons_show("Ordering roster rooms by name");
2750 prefs_set_string(PREF_ROSTER_ROOMS_ORDER, "name");
2751 if (conn_status == JABBER_CONNECTED) {
2752 rosterwin_roster();
2753 }
2754 return TRUE;
2755 } else if (g_strcmp0(args[2], "unread") == 0) {
2756 cons_show("Ordering roster rooms by unread messages");
2757 prefs_set_string(PREF_ROSTER_ROOMS_ORDER, "unread");
2758 if (conn_status == JABBER_CONNECTED) {
2759 rosterwin_roster();
2760 }
2761 return TRUE;
2762 } else {
2763 cons_bad_cmd_usage(command);
2764 return TRUE;
2765 }
2766 } else if (g_strcmp0(args[1], "unread") == 0) {
2767 if (g_strcmp0(args[2], "before") == 0) {
2768 cons_show("Roster rooms unread message count: before");
2769 prefs_set_string(PREF_ROSTER_ROOMS_UNREAD, "before");
2770 if (conn_status == JABBER_CONNECTED) {
2771 rosterwin_roster();
2772 }
2773 return TRUE;
2774 } else if (g_strcmp0(args[2], "after") == 0) {
2775 cons_show("Roster rooms unread message count: after");
2776 prefs_set_string(PREF_ROSTER_ROOMS_UNREAD, "after");
2777 if (conn_status == JABBER_CONNECTED) {
2778 rosterwin_roster();
2779 }
2780 return TRUE;
2781 } else if (g_strcmp0(args[2], "off") == 0) {
2782 cons_show("Roster rooms unread message count: off");
2783 prefs_set_string(PREF_ROSTER_ROOMS_UNREAD, "off");
2784 if (conn_status == JABBER_CONNECTED) {
2785 rosterwin_roster();
2786 }
2787 return TRUE;
2788 } else {
2789 cons_bad_cmd_usage(command);
2790 return TRUE;
2791 }
2792 } else if (g_strcmp0(args[1], "private") == 0) {
2793 if (g_strcmp0(args[2], "char") == 0) {
2794 if (!args[3]) {
2795 cons_bad_cmd_usage(command);
2796 } else if (g_strcmp0(args[3], "none") == 0) {
2797 prefs_clear_roster_room_private_char();
2798 cons_show("Roster room private char removed.");
2799 rosterwin_roster();
2800 } else {
2801 prefs_set_roster_room_private_char(args[3][0]);
2802 cons_show("Roster room private char set to %c.", args[3][0]);
2803 rosterwin_roster();
2804 }
2805 return TRUE;
2806 } else {
2807 cons_bad_cmd_usage(command);
2808 return TRUE;
2809 }
2810 } else if (g_strcmp0(args[1], "by") == 0) {
2811 if (g_strcmp0(args[2], "service") == 0) {
2812 cons_show("Grouping rooms by service");
2813 prefs_set_string(PREF_ROSTER_ROOMS_BY, "service");
2814 if (conn_status == JABBER_CONNECTED) {
2815 rosterwin_roster();
2816 }
2817 return TRUE;
2818 } else if (g_strcmp0(args[2], "none") == 0) {
2819 cons_show("Roster room grouping disabled");
2820 prefs_set_string(PREF_ROSTER_ROOMS_BY, "none");
2821 if (conn_status == JABBER_CONNECTED) {
2822 rosterwin_roster();
2823 }
2824 return TRUE;
2825 } else {
2826 cons_bad_cmd_usage(command);
2827 return TRUE;
2828 }
2829 } else if (g_strcmp0(args[1], "show") == 0) {
2830 if (g_strcmp0(args[2], "server") == 0) {
2831 cons_show("Roster room server enabled.");
2832 prefs_set_boolean(PREF_ROSTER_ROOMS_SERVER, TRUE);
2833 if (conn_status == JABBER_CONNECTED) {
2834 rosterwin_roster();
2835 }
2836 return TRUE;
2837 } else {
2838 cons_bad_cmd_usage(command);
2839 return TRUE;
2840 }
2841 } else if (g_strcmp0(args[1], "hide") == 0) {
2842 if (g_strcmp0(args[2], "server") == 0) {
2843 cons_show("Roster room server disabled.");
2844 prefs_set_boolean(PREF_ROSTER_ROOMS_SERVER, FALSE);
2845 if (conn_status == JABBER_CONNECTED) {
2846 rosterwin_roster();
2847 }
2848 return TRUE;
2849 } else {
2850 cons_bad_cmd_usage(command);
2851 return TRUE;
2852 }
2853 } else if (g_strcmp0(args[1], "use") == 0) {
2854 if (g_strcmp0(args[2], "jid") == 0) {
2855 cons_show("Roster room display jid as name.");
2856 prefs_set_string(PREF_ROSTER_ROOMS_USE_AS_NAME, "jid");
2857 if (conn_status == JABBER_CONNECTED) {
2858 rosterwin_roster();
2859 }
2860 return TRUE;
2861 } else if (g_strcmp0(args[2], "name") == 0) {
2862 cons_show("Roster room display room name as name.");
2863 prefs_set_string(PREF_ROSTER_ROOMS_USE_AS_NAME, "name");
2864 if (conn_status == JABBER_CONNECTED) {
2865 rosterwin_roster();
2866 }
2867 return TRUE;
2868 } else {
2869 cons_bad_cmd_usage(command);
2870 return TRUE;
2871 }
2872 } else {
2873 cons_bad_cmd_usage(command);
2874 return TRUE;
2875 }
2876
2877 // add contact
2878 } else if (strcmp(args[0], "add") == 0) {
2879 if (conn_status != JABBER_CONNECTED) {
2880 cons_show("You are not currently connected.");
2881 return TRUE;
2882 }
2883 char* jid = args[1];
2884 if (jid == NULL) {
2885 cons_bad_cmd_usage(command);
2886 } else {
2887 char* name = args[2];
2888 roster_send_add_new(jid, name);
2889 }
2890 return TRUE;
2891
2892 // remove contact
2893 } else if (strcmp(args[0], "remove") == 0) {
2894 if (conn_status != JABBER_CONNECTED) {
2895 cons_show("You are not currently connected.");
2896 return TRUE;
2897 }
2898 char* jid = args[1];
2899 if (jid == NULL) {
2900 cons_bad_cmd_usage(command);
2901 } else {
2902 roster_send_remove(jid);
2903 }
2904 return TRUE;
2905
2906 } else if (strcmp(args[0], "remove_all") == 0) {
2907 if (g_strcmp0(args[1], "contacts") != 0) {
2908 cons_bad_cmd_usage(command);
2909 return TRUE;
2910 }
2911 if (conn_status != JABBER_CONNECTED) {
2912 cons_show("You are not currently connected.");
2913 return TRUE;
2914 }
2915
2916 GSList* all = roster_get_contacts(ROSTER_ORD_NAME);
2917 GSList* curr = all;
2918 while (curr) {
2919 PContact contact = curr->data;
2920 roster_send_remove(p_contact_barejid(contact));
2921 curr = g_slist_next(curr);
2922 }
2923
2924 g_slist_free(all);
2925 return TRUE;
2926
2927 // change nickname
2928 } else if (strcmp(args[0], "nick") == 0) {
2929 if (conn_status != JABBER_CONNECTED) {
2930 cons_show("You are not currently connected.");
2931 return TRUE;
2932 }
2933 char* jid = args[1];
2934 if (jid == NULL) {
2935 cons_bad_cmd_usage(command);
2936 return TRUE;
2937 }
2938
2939 char* name = args[2];
2940 if (name == NULL) {
2941 cons_bad_cmd_usage(command);
2942 return TRUE;
2943 }
2944
2945 // contact does not exist
2946 PContact contact = roster_get_contact(jid);
2947 if (contact == NULL) {
2948 cons_show("Contact not found in roster: %s", jid);
2949 return TRUE;
2950 }
2951
2952 const char* barejid = p_contact_barejid(contact);
2953
2954 // TODO wait for result stanza before updating
2955 const char* oldnick = p_contact_name(contact);
2956 wins_change_nick(barejid, oldnick, name);
2957 roster_change_name(contact, name);
2958 GSList* groups = p_contact_groups(contact);
2959 roster_send_name_change(barejid, name, groups);
2960
2961 cons_show("Nickname for %s set to: %s.", jid, name);
2962
2963 return TRUE;
2964
2965 // remove nickname
2966 } else if (strcmp(args[0], "clearnick") == 0) {
2967 if (conn_status != JABBER_CONNECTED) {
2968 cons_show("You are not currently connected.");
2969 return TRUE;
2970 }
2971 char* jid = args[1];
2972 if (jid == NULL) {
2973 cons_bad_cmd_usage(command);
2974 return TRUE;
2975 }
2976
2977 // contact does not exist
2978 PContact contact = roster_get_contact(jid);
2979 if (contact == NULL) {
2980 cons_show("Contact not found in roster: %s", jid);
2981 return TRUE;
2982 }
2983
2984 const char* barejid = p_contact_barejid(contact);
2985
2986 // TODO wait for result stanza before updating
2987 const char* oldnick = p_contact_name(contact);
2988 wins_remove_nick(barejid, oldnick);
2989 roster_change_name(contact, NULL);
2990 GSList* groups = p_contact_groups(contact);
2991 roster_send_name_change(barejid, NULL, groups);
2992
2993 cons_show("Nickname for %s removed.", jid);
2994
2995 return TRUE;
2996 } else {
2997 cons_bad_cmd_usage(command);
2998 return TRUE;
2999 }
3000 }
3001
3002 gboolean
cmd_blocked(ProfWin * window,const char * const command,gchar ** args)3003 cmd_blocked(ProfWin* window, const char* const command, gchar** args)
3004 {
3005 if (connection_get_status() != JABBER_CONNECTED) {
3006 cons_show("You are not currently connected.");
3007 return TRUE;
3008 }
3009
3010 if (!connection_supports(XMPP_FEATURE_BLOCKING)) {
3011 cons_show("Blocking (%s) not supported by server.", XMPP_FEATURE_BLOCKING);
3012 return TRUE;
3013 }
3014
3015 blocked_report br = BLOCKED_NO_REPORT;
3016
3017 if (g_strcmp0(args[0], "add") == 0) {
3018 char* jid = args[1];
3019
3020 if (jid == NULL && (window->type == WIN_CHAT)) {
3021 ProfChatWin* chatwin = (ProfChatWin*)window;
3022 jid = chatwin->barejid;
3023 }
3024
3025 if (jid == NULL) {
3026 cons_bad_cmd_usage(command);
3027 return TRUE;
3028 }
3029
3030 gboolean res = blocked_add(jid, br, NULL);
3031 if (!res) {
3032 cons_show("User %s already blocked.", jid);
3033 }
3034
3035 return TRUE;
3036 }
3037
3038 if (g_strcmp0(args[0], "remove") == 0) {
3039 if (args[1] == NULL) {
3040 cons_bad_cmd_usage(command);
3041 return TRUE;
3042 }
3043
3044 gboolean res = blocked_remove(args[1]);
3045 if (!res) {
3046 cons_show("User %s is not currently blocked.", args[1]);
3047 }
3048
3049 return TRUE;
3050 }
3051
3052 if (args[0] && strncmp(args[0], "report-", 7) == 0) {
3053 char* jid = NULL;
3054 char* msg = NULL;
3055 guint argn = g_strv_length(args);
3056
3057 if (argn >= 2) {
3058 jid = args[1];
3059 } else {
3060 cons_bad_cmd_usage(command);
3061 return TRUE;
3062 }
3063
3064 if (argn >= 3) {
3065 msg = args[2];
3066 }
3067
3068 if (args[1] && g_strcmp0(args[0], "report-abuse") == 0) {
3069 br = BLOCKED_REPORT_ABUSE;
3070 } else if (args[1] && g_strcmp0(args[0], "report-spam") == 0) {
3071 br = BLOCKED_REPORT_SPAM;
3072 } else {
3073 cons_bad_cmd_usage(command);
3074 return TRUE;
3075 }
3076
3077 if (!connection_supports(XMPP_FEATURE_SPAM_REPORTING)) {
3078 cons_show("Spam reporting (%s) not supported by server.", XMPP_FEATURE_SPAM_REPORTING);
3079 return TRUE;
3080 }
3081
3082 gboolean res = blocked_add(jid, br, msg);
3083 if (!res) {
3084 cons_show("User %s already blocked.", args[1]);
3085 }
3086 return TRUE;
3087 }
3088
3089 GList* blocked = blocked_list();
3090 GList* curr = blocked;
3091 if (curr) {
3092 cons_show("Blocked users:");
3093 while (curr) {
3094 cons_show(" %s", curr->data);
3095 curr = g_list_next(curr);
3096 }
3097 } else {
3098 cons_show("No blocked users.");
3099 }
3100
3101 return TRUE;
3102 }
3103
3104 gboolean
cmd_resource(ProfWin * window,const char * const command,gchar ** args)3105 cmd_resource(ProfWin* window, const char* const command, gchar** args)
3106 {
3107 char* cmd = args[0];
3108 char* setting = NULL;
3109 if (g_strcmp0(cmd, "message") == 0) {
3110 setting = args[1];
3111 if (!setting) {
3112 cons_bad_cmd_usage(command);
3113 return TRUE;
3114 } else {
3115 _cmd_set_boolean_preference(setting, command, "Message resource", PREF_RESOURCE_MESSAGE);
3116 return TRUE;
3117 }
3118 } else if (g_strcmp0(cmd, "title") == 0) {
3119 setting = args[1];
3120 if (!setting) {
3121 cons_bad_cmd_usage(command);
3122 return TRUE;
3123 } else {
3124 _cmd_set_boolean_preference(setting, command, "Title resource", PREF_RESOURCE_TITLE);
3125 return TRUE;
3126 }
3127 }
3128
3129 if (window->type != WIN_CHAT) {
3130 cons_show("Resource can only be changed in chat windows.");
3131 return TRUE;
3132 }
3133
3134 jabber_conn_status_t conn_status = connection_get_status();
3135 if (conn_status != JABBER_CONNECTED) {
3136 cons_show("You are not currently connected.");
3137 return TRUE;
3138 }
3139
3140 ProfChatWin* chatwin = (ProfChatWin*)window;
3141
3142 if (g_strcmp0(cmd, "set") == 0) {
3143 char* resource = args[1];
3144 if (!resource) {
3145 cons_bad_cmd_usage(command);
3146 return TRUE;
3147 }
3148
3149 #ifdef HAVE_LIBOTR
3150 if (otr_is_secure(chatwin->barejid)) {
3151 cons_show("Cannot choose resource during an OTR session.");
3152 return TRUE;
3153 }
3154 #endif
3155
3156 PContact contact = roster_get_contact(chatwin->barejid);
3157 if (!contact) {
3158 cons_show("Cannot choose resource for contact not in roster.");
3159 return TRUE;
3160 }
3161
3162 if (!p_contact_get_resource(contact, resource)) {
3163 cons_show("No such resource %s.", resource);
3164 return TRUE;
3165 }
3166
3167 chatwin->resource_override = strdup(resource);
3168 chat_state_free(chatwin->state);
3169 chatwin->state = chat_state_new();
3170 chat_session_resource_override(chatwin->barejid, resource);
3171 return TRUE;
3172
3173 } else if (g_strcmp0(cmd, "off") == 0) {
3174 FREE_SET_NULL(chatwin->resource_override);
3175 chat_state_free(chatwin->state);
3176 chatwin->state = chat_state_new();
3177 chat_session_remove(chatwin->barejid);
3178 return TRUE;
3179 } else {
3180 cons_bad_cmd_usage(command);
3181 return TRUE;
3182 }
3183 }
3184
3185 static void
_cmd_status_show_status(char * usr)3186 _cmd_status_show_status(char* usr)
3187 {
3188 char* usr_jid = roster_barejid_from_name(usr);
3189 if (usr_jid == NULL) {
3190 usr_jid = usr;
3191 }
3192 cons_show_status(usr_jid);
3193 }
3194
3195 gboolean
cmd_status_set(ProfWin * window,const char * const command,gchar ** args)3196 cmd_status_set(ProfWin* window, const char* const command, gchar** args)
3197 {
3198 char* state = args[1];
3199
3200 if (g_strcmp0(state, "online") == 0) {
3201 _update_presence(RESOURCE_ONLINE, "online", args);
3202 } else if (g_strcmp0(state, "away") == 0) {
3203 _update_presence(RESOURCE_AWAY, "away", args);
3204 } else if (g_strcmp0(state, "dnd") == 0) {
3205 _update_presence(RESOURCE_DND, "dnd", args);
3206 } else if (g_strcmp0(state, "chat") == 0) {
3207 _update_presence(RESOURCE_CHAT, "chat", args);
3208 } else if (g_strcmp0(state, "xa") == 0) {
3209 _update_presence(RESOURCE_XA, "xa", args);
3210 } else {
3211 cons_bad_cmd_usage(command);
3212 }
3213
3214 return TRUE;
3215 }
3216
3217 gboolean
cmd_status_get(ProfWin * window,const char * const command,gchar ** args)3218 cmd_status_get(ProfWin* window, const char* const command, gchar** args)
3219 {
3220 char* usr = args[1];
3221
3222 jabber_conn_status_t conn_status = connection_get_status();
3223
3224 if (conn_status != JABBER_CONNECTED) {
3225 cons_show("You are not currently connected.");
3226 return TRUE;
3227 }
3228
3229 switch (window->type) {
3230 case WIN_MUC:
3231 if (usr) {
3232 ProfMucWin* mucwin = (ProfMucWin*)window;
3233 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3234 Occupant* occupant = muc_roster_item(mucwin->roomjid, usr);
3235 if (occupant) {
3236 win_show_occupant(window, occupant);
3237 } else {
3238 win_println(window, THEME_DEFAULT, "-", "No such participant \"%s\" in room.", usr);
3239 }
3240 } else {
3241 win_println(window, THEME_DEFAULT, "-", "You must specify a nickname.");
3242 }
3243 break;
3244 case WIN_CHAT:
3245 if (usr) {
3246 _cmd_status_show_status(usr);
3247 } else {
3248 ProfChatWin* chatwin = (ProfChatWin*)window;
3249 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
3250 PContact pcontact = roster_get_contact(chatwin->barejid);
3251 if (pcontact) {
3252 win_show_contact(window, pcontact);
3253 } else {
3254 win_println(window, THEME_DEFAULT, "-", "Error getting contact info.");
3255 }
3256 }
3257 break;
3258 case WIN_PRIVATE:
3259 if (usr) {
3260 _cmd_status_show_status(usr);
3261 } else {
3262 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
3263 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
3264 Jid* jid = jid_create(privatewin->fulljid);
3265 Occupant* occupant = muc_roster_item(jid->barejid, jid->resourcepart);
3266 if (occupant) {
3267 win_show_occupant(window, occupant);
3268 } else {
3269 win_println(window, THEME_DEFAULT, "-", "Error getting contact info.");
3270 }
3271 jid_destroy(jid);
3272 }
3273 break;
3274 case WIN_CONSOLE:
3275 if (usr) {
3276 _cmd_status_show_status(usr);
3277 } else {
3278 cons_bad_cmd_usage(command);
3279 }
3280 break;
3281 default:
3282 break;
3283 }
3284
3285 return TRUE;
3286 }
3287
3288 static void
_cmd_info_show_contact(char * usr)3289 _cmd_info_show_contact(char* usr)
3290 {
3291 char* usr_jid = roster_barejid_from_name(usr);
3292 if (usr_jid == NULL) {
3293 usr_jid = usr;
3294 }
3295 PContact pcontact = roster_get_contact(usr_jid);
3296 if (pcontact) {
3297 cons_show_info(pcontact);
3298 } else {
3299 cons_show("No such contact \"%s\" in roster.", usr);
3300 }
3301 }
3302
3303 gboolean
cmd_info(ProfWin * window,const char * const command,gchar ** args)3304 cmd_info(ProfWin* window, const char* const command, gchar** args)
3305 {
3306 char* usr = args[0];
3307
3308 jabber_conn_status_t conn_status = connection_get_status();
3309
3310 if (conn_status != JABBER_CONNECTED) {
3311 cons_show("You are not currently connected.");
3312 return TRUE;
3313 }
3314
3315 switch (window->type) {
3316 case WIN_MUC:
3317 if (usr) {
3318 ProfMucWin* mucwin = (ProfMucWin*)window;
3319 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3320 Occupant* occupant = muc_roster_item(mucwin->roomjid, usr);
3321 if (occupant) {
3322 win_show_occupant_info(window, mucwin->roomjid, occupant);
3323 } else {
3324 win_println(window, THEME_DEFAULT, "-", "No such occupant \"%s\" in room.", usr);
3325 }
3326 } else {
3327 ProfMucWin* mucwin = (ProfMucWin*)window;
3328 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3329 iq_room_info_request(mucwin->roomjid, TRUE);
3330 mucwin_info(mucwin);
3331 return TRUE;
3332 }
3333 break;
3334 case WIN_CHAT:
3335 if (usr) {
3336 _cmd_info_show_contact(usr);
3337 } else {
3338 ProfChatWin* chatwin = (ProfChatWin*)window;
3339 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
3340 PContact pcontact = roster_get_contact(chatwin->barejid);
3341 if (pcontact) {
3342 win_show_info(window, pcontact);
3343 } else {
3344 win_println(window, THEME_DEFAULT, "-", "Error getting contact info.");
3345 }
3346 }
3347 break;
3348 case WIN_PRIVATE:
3349 if (usr) {
3350 _cmd_info_show_contact(usr);
3351 } else {
3352 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
3353 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
3354 Jid* jid = jid_create(privatewin->fulljid);
3355 Occupant* occupant = muc_roster_item(jid->barejid, jid->resourcepart);
3356 if (occupant) {
3357 win_show_occupant_info(window, jid->barejid, occupant);
3358 } else {
3359 win_println(window, THEME_DEFAULT, "-", "Error getting contact info.");
3360 }
3361 jid_destroy(jid);
3362 }
3363 break;
3364 case WIN_CONSOLE:
3365 if (usr) {
3366 _cmd_info_show_contact(usr);
3367 } else {
3368 cons_bad_cmd_usage(command);
3369 }
3370 break;
3371 default:
3372 break;
3373 }
3374
3375 return TRUE;
3376 }
3377
3378 gboolean
cmd_caps(ProfWin * window,const char * const command,gchar ** args)3379 cmd_caps(ProfWin* window, const char* const command, gchar** args)
3380 {
3381 jabber_conn_status_t conn_status = connection_get_status();
3382 Occupant* occupant = NULL;
3383
3384 if (conn_status != JABBER_CONNECTED) {
3385 cons_show("You are not currently connected.");
3386 return TRUE;
3387 }
3388
3389 switch (window->type) {
3390 case WIN_MUC:
3391 if (args[0]) {
3392 ProfMucWin* mucwin = (ProfMucWin*)window;
3393 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3394 occupant = muc_roster_item(mucwin->roomjid, args[0]);
3395 if (occupant) {
3396 Jid* jidp = jid_create_from_bare_and_resource(mucwin->roomjid, args[0]);
3397 cons_show_caps(jidp->fulljid, occupant->presence);
3398 jid_destroy(jidp);
3399 } else {
3400 cons_show("No such participant \"%s\" in room.", args[0]);
3401 }
3402 } else {
3403 cons_show("No nickname supplied to /caps in chat room.");
3404 }
3405 break;
3406 case WIN_CHAT:
3407 case WIN_CONSOLE:
3408 if (args[0]) {
3409 Jid* jid = jid_create(args[0]);
3410
3411 if (jid->fulljid == NULL) {
3412 cons_show("You must provide a full jid to the /caps command.");
3413 } else {
3414 PContact pcontact = roster_get_contact(jid->barejid);
3415 if (pcontact == NULL) {
3416 cons_show("Contact not found in roster: %s", jid->barejid);
3417 } else {
3418 Resource* resource = p_contact_get_resource(pcontact, jid->resourcepart);
3419 if (resource == NULL) {
3420 cons_show("Could not find resource %s, for contact %s", jid->barejid, jid->resourcepart);
3421 } else {
3422 cons_show_caps(jid->fulljid, resource->presence);
3423 }
3424 }
3425 }
3426 jid_destroy(jid);
3427 } else {
3428 cons_show("You must provide a jid to the /caps command.");
3429 }
3430 break;
3431 case WIN_PRIVATE:
3432 if (args[0]) {
3433 cons_show("No parameter needed to /caps when in private chat.");
3434 } else {
3435 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
3436 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
3437 Jid* jid = jid_create(privatewin->fulljid);
3438 if (jid) {
3439 occupant = muc_roster_item(jid->barejid, jid->resourcepart);
3440 cons_show_caps(jid->resourcepart, occupant->presence);
3441 jid_destroy(jid);
3442 }
3443 }
3444 break;
3445 default:
3446 break;
3447 }
3448
3449 return TRUE;
3450 }
3451
3452 static void
_send_software_version_iq_to_fulljid(char * request)3453 _send_software_version_iq_to_fulljid(char* request)
3454 {
3455 char* mybarejid = connection_get_barejid();
3456 Jid* jid = jid_create(request);
3457
3458 if (jid == NULL || jid->fulljid == NULL) {
3459 cons_show("You must provide a full jid to the /software command.");
3460 } else if (g_strcmp0(jid->barejid, mybarejid) == 0) {
3461 cons_show("Cannot request software version for yourself.");
3462 } else {
3463 iq_send_software_version(jid->fulljid);
3464 }
3465 free(mybarejid);
3466 jid_destroy(jid);
3467 }
3468
3469 gboolean
cmd_software(ProfWin * window,const char * const command,gchar ** args)3470 cmd_software(ProfWin* window, const char* const command, gchar** args)
3471 {
3472 jabber_conn_status_t conn_status = connection_get_status();
3473
3474 if (conn_status != JABBER_CONNECTED) {
3475 cons_show("You are not currently connected.");
3476 return TRUE;
3477 }
3478
3479 switch (window->type) {
3480 case WIN_MUC:
3481 if (args[0]) {
3482 ProfMucWin* mucwin = (ProfMucWin*)window;
3483 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3484 Occupant* occupant = muc_roster_item(mucwin->roomjid, args[0]);
3485 if (occupant) {
3486 Jid* jid = jid_create_from_bare_and_resource(mucwin->roomjid, args[0]);
3487 iq_send_software_version(jid->fulljid);
3488 jid_destroy(jid);
3489 } else {
3490 cons_show("No such participant \"%s\" in room.", args[0]);
3491 }
3492 } else {
3493 cons_show("No nickname supplied to /software in chat room.");
3494 }
3495 break;
3496 case WIN_CHAT:
3497 if (args[0]) {
3498 _send_software_version_iq_to_fulljid(args[0]);
3499 break;
3500 } else {
3501 ProfChatWin* chatwin = (ProfChatWin*)window;
3502 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
3503
3504 char* resource = NULL;
3505 ChatSession* session = chat_session_get(chatwin->barejid);
3506 if (chatwin->resource_override) {
3507 resource = chatwin->resource_override;
3508 } else if (session && session->resource) {
3509 resource = session->resource;
3510 }
3511
3512 if (resource) {
3513 GString* fulljid = g_string_new(chatwin->barejid);
3514 g_string_append_printf(fulljid, "/%s", resource);
3515 iq_send_software_version(fulljid->str);
3516 g_string_free(fulljid, TRUE);
3517 } else {
3518 win_println(window, THEME_DEFAULT, "-", "Unknown resource for /software command. See /help resource.");
3519 }
3520 break;
3521 }
3522 case WIN_CONSOLE:
3523 if (args[0]) {
3524 _send_software_version_iq_to_fulljid(args[0]);
3525 } else {
3526 cons_show("You must provide a jid to the /software command.");
3527 }
3528 break;
3529 case WIN_PRIVATE:
3530 if (args[0]) {
3531 cons_show("No parameter needed to /software when in private chat.");
3532 } else {
3533 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
3534 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
3535 iq_send_software_version(privatewin->fulljid);
3536 }
3537 break;
3538 default:
3539 break;
3540 }
3541
3542 return TRUE;
3543 }
3544
3545 gboolean
cmd_serversoftware(ProfWin * window,const char * const command,gchar ** args)3546 cmd_serversoftware(ProfWin* window, const char* const command, gchar** args)
3547 {
3548 jabber_conn_status_t conn_status = connection_get_status();
3549
3550 if (conn_status != JABBER_CONNECTED) {
3551 cons_show("You are not currently connected.");
3552 return TRUE;
3553 }
3554
3555 if (args[0]) {
3556 iq_send_software_version(args[0]);
3557 } else {
3558 cons_show("You must provide a jid to the /serversoftware command.");
3559 }
3560
3561 return TRUE;
3562 }
3563
3564 gboolean
cmd_join(ProfWin * window,const char * const command,gchar ** args)3565 cmd_join(ProfWin* window, const char* const command, gchar** args)
3566 {
3567 jabber_conn_status_t conn_status = connection_get_status();
3568 if (conn_status != JABBER_CONNECTED) {
3569 cons_show("You are not currently connected.");
3570 return TRUE;
3571 }
3572
3573 if (args[0] == NULL) {
3574 char* account_name = session_get_account_name();
3575 ProfAccount* account = accounts_get_account(account_name);
3576 if (account->muc_service) {
3577 GString* room_str = g_string_new("");
3578 char* uuid = connection_create_uuid();
3579 g_string_append_printf(room_str, "private-chat-%s@%s", uuid, account->muc_service);
3580 connection_free_uuid(uuid);
3581
3582 presence_join_room(room_str->str, account->muc_nick, NULL);
3583 muc_join(room_str->str, account->muc_nick, NULL, FALSE);
3584
3585 g_string_free(room_str, TRUE);
3586 account_free(account);
3587 } else {
3588 cons_show("Account MUC service property not found.");
3589 }
3590
3591 return TRUE;
3592 }
3593
3594 Jid* room_arg = jid_create(args[0]);
3595 if (room_arg == NULL) {
3596 cons_show_error("Specified room has incorrect format.");
3597 cons_show("");
3598 return TRUE;
3599 }
3600
3601 char* room = NULL;
3602 char* nick = NULL;
3603 char* passwd = NULL;
3604 char* account_name = session_get_account_name();
3605 ProfAccount* account = accounts_get_account(account_name);
3606
3607 // full room jid supplied (room@server)
3608 if (room_arg->localpart) {
3609 room = g_strdup(args[0]);
3610
3611 // server not supplied (room), use account preference
3612 } else if (account->muc_service) {
3613 GString* room_str = g_string_new("");
3614 g_string_append(room_str, args[0]);
3615 g_string_append(room_str, "@");
3616 g_string_append(room_str, account->muc_service);
3617 room = room_str->str;
3618 g_string_free(room_str, FALSE);
3619
3620 // no account preference
3621 } else {
3622 cons_show("Account MUC service property not found.");
3623 return TRUE;
3624 }
3625
3626 // Additional args supplied
3627 gchar* opt_keys[] = { "nick", "password", NULL };
3628 gboolean parsed;
3629
3630 GHashTable* options = parse_options(&args[1], opt_keys, &parsed);
3631 if (!parsed) {
3632 cons_bad_cmd_usage(command);
3633 cons_show("");
3634 g_free(room);
3635 jid_destroy(room_arg);
3636 return TRUE;
3637 }
3638
3639 nick = g_hash_table_lookup(options, "nick");
3640 passwd = g_hash_table_lookup(options, "password");
3641
3642 options_destroy(options);
3643
3644 // In the case that a nick wasn't provided by the optional args...
3645 if (!nick) {
3646 nick = account->muc_nick;
3647 }
3648
3649 // When no password, check for invite with password
3650 if (!passwd) {
3651 passwd = muc_invite_password(room);
3652 }
3653
3654 if (!muc_active(room)) {
3655 presence_join_room(room, nick, passwd);
3656 muc_join(room, nick, passwd, FALSE);
3657 iq_room_affiliation_list(room, "member", false);
3658 iq_room_affiliation_list(room, "admin", false);
3659 iq_room_affiliation_list(room, "owner", false);
3660 } else if (muc_roster_complete(room)) {
3661 ui_switch_to_room(room);
3662 }
3663
3664 g_free(room);
3665 jid_destroy(room_arg);
3666 account_free(account);
3667
3668 return TRUE;
3669 }
3670
3671 gboolean
cmd_invite(ProfWin * window,const char * const command,gchar ** args)3672 cmd_invite(ProfWin* window, const char* const command, gchar** args)
3673 {
3674 jabber_conn_status_t conn_status = connection_get_status();
3675
3676 if (conn_status != JABBER_CONNECTED) {
3677 cons_show("You are not currently connected.");
3678 return TRUE;
3679 }
3680
3681 if (g_strcmp0(args[0], "send") == 0) {
3682 char* contact = args[1];
3683 char* reason = args[2];
3684
3685 if (window->type != WIN_MUC) {
3686 cons_show("You must be in a chat room to send an invite.");
3687 return TRUE;
3688 }
3689
3690 char* usr_jid = roster_barejid_from_name(contact);
3691 if (usr_jid == NULL) {
3692 usr_jid = contact;
3693 }
3694
3695 ProfMucWin* mucwin = (ProfMucWin*)window;
3696 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
3697 message_send_invite(mucwin->roomjid, usr_jid, reason);
3698 if (reason) {
3699 cons_show("Room invite sent, contact: %s, room: %s, reason: \"%s\".",
3700 contact, mucwin->roomjid, reason);
3701 } else {
3702 cons_show("Room invite sent, contact: %s, room: %s.",
3703 contact, mucwin->roomjid);
3704 }
3705 } else if (g_strcmp0(args[0], "list") == 0) {
3706 GList* invites = muc_invites();
3707 cons_show_room_invites(invites);
3708 g_list_free_full(invites, g_free);
3709 } else if (g_strcmp0(args[0], "decline") == 0) {
3710 if (!muc_invites_contain(args[1])) {
3711 cons_show("No such invite exists.");
3712 } else {
3713 muc_invites_remove(args[1]);
3714 cons_show("Declined invite to %s.", args[1]);
3715 }
3716 }
3717
3718 return TRUE;
3719 }
3720
3721 gboolean
cmd_form_field(ProfWin * window,char * tag,gchar ** args)3722 cmd_form_field(ProfWin* window, char* tag, gchar** args)
3723 {
3724 if (window->type != WIN_CONFIG) {
3725 return TRUE;
3726 }
3727
3728 ProfConfWin* confwin = (ProfConfWin*)window;
3729 DataForm* form = confwin->form;
3730 if (form) {
3731 if (!form_tag_exists(form, tag)) {
3732 win_println(window, THEME_DEFAULT, "-", "Form does not contain a field with tag %s", tag);
3733 return TRUE;
3734 }
3735
3736 form_field_type_t field_type = form_get_field_type(form, tag);
3737 char* cmd = NULL;
3738 char* value = NULL;
3739 gboolean valid = FALSE;
3740 gboolean added = FALSE;
3741 gboolean removed = FALSE;
3742
3743 switch (field_type) {
3744 case FIELD_BOOLEAN:
3745 value = args[0];
3746 if (g_strcmp0(value, "on") == 0) {
3747 form_set_value(form, tag, "1");
3748 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3749 confwin_show_form_field(confwin, form, tag);
3750 } else if (g_strcmp0(value, "off") == 0) {
3751 form_set_value(form, tag, "0");
3752 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3753 confwin_show_form_field(confwin, form, tag);
3754 } else {
3755 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3756 confwin_field_help(confwin, tag);
3757 win_println(window, THEME_DEFAULT, "-", "");
3758 }
3759 break;
3760
3761 case FIELD_TEXT_PRIVATE:
3762 case FIELD_TEXT_SINGLE:
3763 case FIELD_JID_SINGLE:
3764 value = args[0];
3765 if (value == NULL) {
3766 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3767 confwin_field_help(confwin, tag);
3768 win_println(window, THEME_DEFAULT, "-", "");
3769 } else {
3770 form_set_value(form, tag, value);
3771 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3772 confwin_show_form_field(confwin, form, tag);
3773 }
3774 break;
3775 case FIELD_LIST_SINGLE:
3776 value = args[0];
3777 if ((value == NULL) || !form_field_contains_option(form, tag, value)) {
3778 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3779 confwin_field_help(confwin, tag);
3780 win_println(window, THEME_DEFAULT, "-", "");
3781 } else {
3782 form_set_value(form, tag, value);
3783 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3784 confwin_show_form_field(confwin, form, tag);
3785 }
3786 break;
3787
3788 case FIELD_TEXT_MULTI:
3789 cmd = args[0];
3790 if (cmd) {
3791 value = args[1];
3792 }
3793 if ((g_strcmp0(cmd, "add") != 0) && (g_strcmp0(cmd, "remove"))) {
3794 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3795 confwin_field_help(confwin, tag);
3796 win_println(window, THEME_DEFAULT, "-", "");
3797 break;
3798 }
3799 if (value == NULL) {
3800 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3801 confwin_field_help(confwin, tag);
3802 win_println(window, THEME_DEFAULT, "-", "");
3803 break;
3804 }
3805 if (g_strcmp0(cmd, "add") == 0) {
3806 form_add_value(form, tag, value);
3807 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3808 confwin_show_form_field(confwin, form, tag);
3809 break;
3810 }
3811 if (g_strcmp0(args[0], "remove") == 0) {
3812 if (!g_str_has_prefix(value, "val")) {
3813 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3814 confwin_field_help(confwin, tag);
3815 win_println(window, THEME_DEFAULT, "-", "");
3816 break;
3817 }
3818 if (strlen(value) < 4) {
3819 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3820 confwin_field_help(confwin, tag);
3821 win_println(window, THEME_DEFAULT, "-", "");
3822 break;
3823 }
3824
3825 int index = strtol(&value[3], NULL, 10);
3826 if ((index < 1) || (index > form_get_value_count(form, tag))) {
3827 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3828 confwin_field_help(confwin, tag);
3829 win_println(window, THEME_DEFAULT, "-", "");
3830 break;
3831 }
3832
3833 removed = form_remove_text_multi_value(form, tag, index);
3834 if (removed) {
3835 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3836 confwin_show_form_field(confwin, form, tag);
3837 } else {
3838 win_println(window, THEME_DEFAULT, "-", "Could not remove %s from %s", value, tag);
3839 }
3840 }
3841 break;
3842 case FIELD_LIST_MULTI:
3843 cmd = args[0];
3844 if (cmd) {
3845 value = args[1];
3846 }
3847 if ((g_strcmp0(cmd, "add") != 0) && (g_strcmp0(cmd, "remove"))) {
3848 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3849 confwin_field_help(confwin, tag);
3850 win_println(window, THEME_DEFAULT, "-", "");
3851 break;
3852 }
3853 if (value == NULL) {
3854 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3855 confwin_field_help(confwin, tag);
3856 win_println(window, THEME_DEFAULT, "-", "");
3857 break;
3858 }
3859 if (g_strcmp0(args[0], "add") == 0) {
3860 valid = form_field_contains_option(form, tag, value);
3861 if (valid) {
3862 added = form_add_unique_value(form, tag, value);
3863 if (added) {
3864 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3865 confwin_show_form_field(confwin, form, tag);
3866 } else {
3867 win_println(window, THEME_DEFAULT, "-", "Value %s already selected for %s", value, tag);
3868 }
3869 } else {
3870 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3871 confwin_field_help(confwin, tag);
3872 win_println(window, THEME_DEFAULT, "-", "");
3873 }
3874 break;
3875 }
3876 if (g_strcmp0(args[0], "remove") == 0) {
3877 valid = form_field_contains_option(form, tag, value);
3878 if (valid == TRUE) {
3879 removed = form_remove_value(form, tag, value);
3880 if (removed) {
3881 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3882 confwin_show_form_field(confwin, form, tag);
3883 } else {
3884 win_println(window, THEME_DEFAULT, "-", "Value %s is not currently set for %s", value, tag);
3885 }
3886 } else {
3887 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3888 confwin_field_help(confwin, tag);
3889 win_println(window, THEME_DEFAULT, "-", "");
3890 }
3891 }
3892 break;
3893 case FIELD_JID_MULTI:
3894 cmd = args[0];
3895 if (cmd) {
3896 value = args[1];
3897 }
3898 if ((g_strcmp0(cmd, "add") != 0) && (g_strcmp0(cmd, "remove"))) {
3899 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3900 confwin_field_help(confwin, tag);
3901 win_println(window, THEME_DEFAULT, "-", "");
3902 break;
3903 }
3904 if (value == NULL) {
3905 win_println(window, THEME_DEFAULT, "-", "Invalid command, usage:");
3906 confwin_field_help(confwin, tag);
3907 win_println(window, THEME_DEFAULT, "-", "");
3908 break;
3909 }
3910 if (g_strcmp0(args[0], "add") == 0) {
3911 added = form_add_unique_value(form, tag, value);
3912 if (added) {
3913 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3914 confwin_show_form_field(confwin, form, tag);
3915 } else {
3916 win_println(window, THEME_DEFAULT, "-", "JID %s already exists in %s", value, tag);
3917 }
3918 break;
3919 }
3920 if (g_strcmp0(args[0], "remove") == 0) {
3921 removed = form_remove_value(form, tag, value);
3922 if (removed) {
3923 win_println(window, THEME_DEFAULT, "-", "Field updated...");
3924 confwin_show_form_field(confwin, form, tag);
3925 } else {
3926 win_println(window, THEME_DEFAULT, "-", "Field %s does not contain %s", tag, value);
3927 }
3928 }
3929 break;
3930
3931 default:
3932 break;
3933 }
3934 }
3935
3936 return TRUE;
3937 }
3938
3939 gboolean
cmd_form(ProfWin * window,const char * const command,gchar ** args)3940 cmd_form(ProfWin* window, const char* const command, gchar** args)
3941 {
3942 jabber_conn_status_t conn_status = connection_get_status();
3943
3944 if (conn_status != JABBER_CONNECTED) {
3945 cons_show("You are not currently connected.");
3946 return TRUE;
3947 }
3948
3949 if (window->type != WIN_CONFIG) {
3950 cons_show("Command '/form' does not apply to this window.");
3951 return TRUE;
3952 }
3953
3954 if ((g_strcmp0(args[0], "submit") != 0) && (g_strcmp0(args[0], "cancel") != 0) && (g_strcmp0(args[0], "show") != 0) && (g_strcmp0(args[0], "help") != 0)) {
3955 cons_bad_cmd_usage(command);
3956 return TRUE;
3957 }
3958
3959 ProfConfWin* confwin = (ProfConfWin*)window;
3960 assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
3961
3962 if (g_strcmp0(args[0], "show") == 0) {
3963 confwin_show_form(confwin);
3964 return TRUE;
3965 }
3966
3967 if (g_strcmp0(args[0], "help") == 0) {
3968 char* tag = args[1];
3969 if (tag) {
3970 confwin_field_help(confwin, tag);
3971 } else {
3972 confwin_form_help(confwin);
3973
3974 gchar** help_text = NULL;
3975 Command* command = cmd_get("/form");
3976
3977 if (command) {
3978 help_text = command->help.synopsis;
3979 }
3980
3981 ui_show_lines((ProfWin*)confwin, help_text);
3982 }
3983 win_println(window, THEME_DEFAULT, "-", "");
3984 return TRUE;
3985 }
3986
3987 if (g_strcmp0(args[0], "submit") == 0 && confwin->submit != NULL) {
3988 confwin->submit(confwin);
3989 }
3990
3991 if (g_strcmp0(args[0], "cancel") == 0 && confwin->cancel != NULL) {
3992 confwin->cancel(confwin);
3993 }
3994
3995 if ((g_strcmp0(args[0], "submit") == 0) || (g_strcmp0(args[0], "cancel") == 0)) {
3996 if (confwin->form) {
3997 cmd_ac_remove_form_fields(confwin->form);
3998 }
3999
4000 int num = wins_get_num(window);
4001
4002 ProfWin* new_current = (ProfWin*)wins_get_muc(confwin->roomjid);
4003 if (!new_current) {
4004 new_current = wins_get_console();
4005 }
4006 ui_focus_win(new_current);
4007 wins_close_by_num(num);
4008 wins_tidy();
4009 }
4010
4011 return TRUE;
4012 }
4013
4014 gboolean
cmd_kick(ProfWin * window,const char * const command,gchar ** args)4015 cmd_kick(ProfWin* window, const char* const command, gchar** args)
4016 {
4017 jabber_conn_status_t conn_status = connection_get_status();
4018
4019 if (conn_status != JABBER_CONNECTED) {
4020 cons_show("You are not currently connected.");
4021 return TRUE;
4022 }
4023
4024 if (window->type != WIN_MUC) {
4025 cons_show("Command '/kick' only applies in chat rooms.");
4026 return TRUE;
4027 }
4028
4029 ProfMucWin* mucwin = (ProfMucWin*)window;
4030 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4031
4032 char* nick = args[0];
4033 if (nick) {
4034 if (muc_roster_contains_nick(mucwin->roomjid, nick)) {
4035 char* reason = args[1];
4036 iq_room_kick_occupant(mucwin->roomjid, nick, reason);
4037 } else {
4038 win_println(window, THEME_DEFAULT, "!", "Occupant does not exist: %s", nick);
4039 }
4040 } else {
4041 cons_bad_cmd_usage(command);
4042 }
4043
4044 return TRUE;
4045 }
4046
4047 gboolean
cmd_ban(ProfWin * window,const char * const command,gchar ** args)4048 cmd_ban(ProfWin* window, const char* const command, gchar** args)
4049 {
4050 jabber_conn_status_t conn_status = connection_get_status();
4051
4052 if (conn_status != JABBER_CONNECTED) {
4053 cons_show("You are not currently connected.");
4054 return TRUE;
4055 }
4056
4057 if (window->type != WIN_MUC) {
4058 cons_show("Command '/ban' only applies in chat rooms.");
4059 return TRUE;
4060 }
4061
4062 ProfMucWin* mucwin = (ProfMucWin*)window;
4063 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4064
4065 char* jid = args[0];
4066 if (jid) {
4067 char* reason = args[1];
4068 iq_room_affiliation_set(mucwin->roomjid, jid, "outcast", reason);
4069 } else {
4070 cons_bad_cmd_usage(command);
4071 }
4072 return TRUE;
4073 }
4074
4075 gboolean
cmd_subject(ProfWin * window,const char * const command,gchar ** args)4076 cmd_subject(ProfWin* window, const char* const command, gchar** args)
4077 {
4078 jabber_conn_status_t conn_status = connection_get_status();
4079
4080 if (conn_status != JABBER_CONNECTED) {
4081 cons_show("You are not currently connected.");
4082 return TRUE;
4083 }
4084
4085 if (window->type != WIN_MUC) {
4086 cons_show("Command '/room' does not apply to this window.");
4087 return TRUE;
4088 }
4089
4090 ProfMucWin* mucwin = (ProfMucWin*)window;
4091 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4092
4093 if (args[0] == NULL) {
4094 char* subject = muc_subject(mucwin->roomjid);
4095 if (subject) {
4096 win_print(window, THEME_ROOMINFO, "!", "Room subject: ");
4097 win_appendln(window, THEME_DEFAULT, "%s", subject);
4098 } else {
4099 win_println(window, THEME_ROOMINFO, "!", "Room has no subject");
4100 }
4101 return TRUE;
4102 }
4103
4104 if (g_strcmp0(args[0], "set") == 0) {
4105 if (args[1]) {
4106 message_send_groupchat_subject(mucwin->roomjid, args[1]);
4107 } else {
4108 cons_bad_cmd_usage(command);
4109 }
4110 return TRUE;
4111 }
4112
4113 if (g_strcmp0(args[0], "edit") == 0) {
4114 if (args[1]) {
4115 message_send_groupchat_subject(mucwin->roomjid, args[1]);
4116 } else {
4117 cons_bad_cmd_usage(command);
4118 }
4119 return TRUE;
4120 }
4121
4122 if (g_strcmp0(args[0], "prepend") == 0) {
4123 if (args[1]) {
4124 char* old_subject = muc_subject(mucwin->roomjid);
4125 if (old_subject) {
4126 GString* new_subject = g_string_new(args[1]);
4127 g_string_append(new_subject, old_subject);
4128 message_send_groupchat_subject(mucwin->roomjid, new_subject->str);
4129 g_string_free(new_subject, TRUE);
4130 } else {
4131 win_print(window, THEME_ROOMINFO, "!", "Room does not have a subject, use /subject set <subject>");
4132 }
4133 } else {
4134 cons_bad_cmd_usage(command);
4135 }
4136 return TRUE;
4137 }
4138
4139 if (g_strcmp0(args[0], "append") == 0) {
4140 if (args[1]) {
4141 char* old_subject = muc_subject(mucwin->roomjid);
4142 if (old_subject) {
4143 GString* new_subject = g_string_new(old_subject);
4144 g_string_append(new_subject, args[1]);
4145 message_send_groupchat_subject(mucwin->roomjid, new_subject->str);
4146 g_string_free(new_subject, TRUE);
4147 } else {
4148 win_print(window, THEME_ROOMINFO, "!", "Room does not have a subject, use /subject set <subject>");
4149 }
4150 } else {
4151 cons_bad_cmd_usage(command);
4152 }
4153 return TRUE;
4154 }
4155
4156 if (g_strcmp0(args[0], "clear") == 0) {
4157 message_send_groupchat_subject(mucwin->roomjid, NULL);
4158 return TRUE;
4159 }
4160
4161 cons_bad_cmd_usage(command);
4162 return TRUE;
4163 }
4164
4165 gboolean
cmd_affiliation(ProfWin * window,const char * const command,gchar ** args)4166 cmd_affiliation(ProfWin* window, const char* const command, gchar** args)
4167 {
4168 jabber_conn_status_t conn_status = connection_get_status();
4169
4170 if (conn_status != JABBER_CONNECTED) {
4171 cons_show("You are not currently connected.");
4172 return TRUE;
4173 }
4174
4175 if (window->type != WIN_MUC) {
4176 cons_show("Command '/affiliation' does not apply to this window.");
4177 return TRUE;
4178 }
4179
4180 char* cmd = args[0];
4181 if (cmd == NULL) {
4182 cons_bad_cmd_usage(command);
4183 return TRUE;
4184 }
4185
4186 char* affiliation = args[1];
4187 if (affiliation && (g_strcmp0(affiliation, "owner") != 0) && (g_strcmp0(affiliation, "admin") != 0) && (g_strcmp0(affiliation, "member") != 0) && (g_strcmp0(affiliation, "none") != 0) && (g_strcmp0(affiliation, "outcast") != 0)) {
4188 cons_bad_cmd_usage(command);
4189 return TRUE;
4190 }
4191
4192 ProfMucWin* mucwin = (ProfMucWin*)window;
4193 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4194
4195 if (g_strcmp0(cmd, "list") == 0) {
4196 if (!affiliation) {
4197 iq_room_affiliation_list(mucwin->roomjid, "owner", true);
4198 iq_room_affiliation_list(mucwin->roomjid, "admin", true);
4199 iq_room_affiliation_list(mucwin->roomjid, "member", true);
4200 iq_room_affiliation_list(mucwin->roomjid, "outcast", true);
4201 } else if (g_strcmp0(affiliation, "none") == 0) {
4202 win_println(window, THEME_DEFAULT, "!", "Cannot list users with no affiliation.");
4203 } else {
4204 iq_room_affiliation_list(mucwin->roomjid, affiliation, true);
4205 }
4206 return TRUE;
4207 }
4208
4209 if (g_strcmp0(cmd, "set") == 0) {
4210 if (!affiliation) {
4211 cons_bad_cmd_usage(command);
4212 return TRUE;
4213 }
4214
4215 char* jid = args[2];
4216 if (jid == NULL) {
4217 cons_bad_cmd_usage(command);
4218 return TRUE;
4219 } else {
4220 char* reason = args[3];
4221 iq_room_affiliation_set(mucwin->roomjid, jid, affiliation, reason);
4222 return TRUE;
4223 }
4224 }
4225
4226 if (g_strcmp0(cmd, "request") == 0) {
4227 message_request_voice(mucwin->roomjid);
4228 return TRUE;
4229 }
4230
4231 if (g_strcmp0(cmd, "register") == 0) {
4232 iq_muc_register_nick(mucwin->roomjid);
4233 return TRUE;
4234 }
4235
4236 cons_bad_cmd_usage(command);
4237 return TRUE;
4238 }
4239
4240 gboolean
cmd_role(ProfWin * window,const char * const command,gchar ** args)4241 cmd_role(ProfWin* window, const char* const command, gchar** args)
4242 {
4243 jabber_conn_status_t conn_status = connection_get_status();
4244
4245 if (conn_status != JABBER_CONNECTED) {
4246 cons_show("You are not currently connected.");
4247 return TRUE;
4248 }
4249
4250 if (window->type != WIN_MUC) {
4251 cons_show("Command '/role' does not apply to this window.");
4252 return TRUE;
4253 }
4254
4255 char* cmd = args[0];
4256 if (cmd == NULL) {
4257 cons_bad_cmd_usage(command);
4258 return TRUE;
4259 }
4260
4261 char* role = args[1];
4262 if (role && (g_strcmp0(role, "visitor") != 0) && (g_strcmp0(role, "participant") != 0) && (g_strcmp0(role, "moderator") != 0) && (g_strcmp0(role, "none") != 0)) {
4263 cons_bad_cmd_usage(command);
4264 return TRUE;
4265 }
4266
4267 ProfMucWin* mucwin = (ProfMucWin*)window;
4268 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4269
4270 if (g_strcmp0(cmd, "list") == 0) {
4271 if (!role) {
4272 iq_room_role_list(mucwin->roomjid, "moderator");
4273 iq_room_role_list(mucwin->roomjid, "participant");
4274 iq_room_role_list(mucwin->roomjid, "visitor");
4275 } else if (g_strcmp0(role, "none") == 0) {
4276 win_println(window, THEME_DEFAULT, "!", "Cannot list users with no role.");
4277 } else {
4278 iq_room_role_list(mucwin->roomjid, role);
4279 }
4280 return TRUE;
4281 }
4282
4283 if (g_strcmp0(cmd, "set") == 0) {
4284 if (!role) {
4285 cons_bad_cmd_usage(command);
4286 return TRUE;
4287 }
4288
4289 char* nick = args[2];
4290 if (nick == NULL) {
4291 cons_bad_cmd_usage(command);
4292 return TRUE;
4293 } else {
4294 char* reason = args[3];
4295 iq_room_role_set(mucwin->roomjid, nick, role, reason);
4296 return TRUE;
4297 }
4298 }
4299
4300 cons_bad_cmd_usage(command);
4301 return TRUE;
4302 }
4303
4304 gboolean
cmd_room(ProfWin * window,const char * const command,gchar ** args)4305 cmd_room(ProfWin* window, const char* const command, gchar** args)
4306 {
4307 jabber_conn_status_t conn_status = connection_get_status();
4308
4309 if (conn_status != JABBER_CONNECTED) {
4310 cons_show("You are not currently connected.");
4311 return TRUE;
4312 }
4313
4314 if (window->type != WIN_MUC) {
4315 cons_show("Command '/room' does not apply to this window.");
4316 return TRUE;
4317 }
4318
4319 ProfMucWin* mucwin = (ProfMucWin*)window;
4320 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4321
4322 if (g_strcmp0(args[0], "accept") == 0) {
4323 gboolean requires_config = muc_requires_config(mucwin->roomjid);
4324 if (!requires_config) {
4325 win_println(window, THEME_ROOMINFO, "!", "Current room does not require configuration.");
4326 return TRUE;
4327 } else {
4328 iq_confirm_instant_room(mucwin->roomjid);
4329 muc_set_requires_config(mucwin->roomjid, FALSE);
4330 win_println(window, THEME_ROOMINFO, "!", "Room unlocked.");
4331 return TRUE;
4332 }
4333 } else if (g_strcmp0(args[0], "destroy") == 0) {
4334 iq_destroy_room(mucwin->roomjid);
4335 return TRUE;
4336 } else if (g_strcmp0(args[0], "config") == 0) {
4337 ProfConfWin* confwin = wins_get_conf(mucwin->roomjid);
4338
4339 if (confwin) {
4340 ui_focus_win((ProfWin*)confwin);
4341 } else {
4342 iq_request_room_config_form(mucwin->roomjid);
4343 }
4344 return TRUE;
4345 } else {
4346 cons_bad_cmd_usage(command);
4347 }
4348
4349 return TRUE;
4350 }
4351
4352 gboolean
cmd_occupants(ProfWin * window,const char * const command,gchar ** args)4353 cmd_occupants(ProfWin* window, const char* const command, gchar** args)
4354 {
4355 if (g_strcmp0(args[0], "size") == 0) {
4356 if (!args[1]) {
4357 cons_bad_cmd_usage(command);
4358 return TRUE;
4359 } else {
4360 int intval = 0;
4361 char* err_msg = NULL;
4362 gboolean res = strtoi_range(args[1], &intval, 1, 99, &err_msg);
4363 if (res) {
4364 prefs_set_occupants_size(intval);
4365 cons_show("Occupants screen size set to: %d%%", intval);
4366 wins_resize_all();
4367 return TRUE;
4368 } else {
4369 cons_show(err_msg);
4370 free(err_msg);
4371 return TRUE;
4372 }
4373 }
4374 }
4375
4376 if (g_strcmp0(args[0], "indent") == 0) {
4377 if (!args[1]) {
4378 cons_bad_cmd_usage(command);
4379 return TRUE;
4380 } else {
4381 int intval = 0;
4382 char* err_msg = NULL;
4383 gboolean res = strtoi_range(args[1], &intval, 0, 10, &err_msg);
4384 if (res) {
4385 prefs_set_occupants_indent(intval);
4386 cons_show("Occupants indent set to: %d", intval);
4387
4388 occupantswin_occupants_all();
4389 } else {
4390 cons_show(err_msg);
4391 free(err_msg);
4392 }
4393 return TRUE;
4394 }
4395 }
4396
4397 if (g_strcmp0(args[0], "wrap") == 0) {
4398 if (!args[1]) {
4399 cons_bad_cmd_usage(command);
4400 return TRUE;
4401 } else {
4402 _cmd_set_boolean_preference(args[1], command, "Occupants panel line wrap", PREF_OCCUPANTS_WRAP);
4403 occupantswin_occupants_all();
4404 return TRUE;
4405 }
4406 }
4407
4408 if (g_strcmp0(args[0], "char") == 0) {
4409 if (!args[1]) {
4410 cons_bad_cmd_usage(command);
4411 } else if (g_strcmp0(args[1], "none") == 0) {
4412 prefs_clear_occupants_char();
4413 cons_show("Occupants char removed.");
4414
4415 occupantswin_occupants_all();
4416 } else {
4417 prefs_set_occupants_char(args[1][0]);
4418 cons_show("Occupants char set to %c.", args[1][0]);
4419
4420 occupantswin_occupants_all();
4421 }
4422 return TRUE;
4423 }
4424
4425 if (g_strcmp0(args[0], "color") == 0) {
4426 _cmd_set_boolean_preference(args[1], command, "Occupants consistent colors", PREF_OCCUPANTS_COLOR_NICK);
4427 occupantswin_occupants_all();
4428 return TRUE;
4429 }
4430
4431 if (g_strcmp0(args[0], "default") == 0) {
4432 if (g_strcmp0(args[1], "show") == 0) {
4433 if (g_strcmp0(args[2], "jid") == 0) {
4434 cons_show("Occupant jids enabled.");
4435 prefs_set_boolean(PREF_OCCUPANTS_JID, TRUE);
4436 } else if (g_strcmp0(args[2], "offline") == 0) {
4437 cons_show("Occupants offline enabled.");
4438 prefs_set_boolean(PREF_OCCUPANTS_OFFLINE, TRUE);
4439 } else {
4440 cons_show("Occupant list enabled.");
4441 prefs_set_boolean(PREF_OCCUPANTS, TRUE);
4442 }
4443 return TRUE;
4444 } else if (g_strcmp0(args[1], "hide") == 0) {
4445 if (g_strcmp0(args[2], "jid") == 0) {
4446 cons_show("Occupant jids disabled.");
4447 prefs_set_boolean(PREF_OCCUPANTS_JID, FALSE);
4448 } else if (g_strcmp0(args[2], "offline") == 0) {
4449 cons_show("Occupants offline disabled.");
4450 prefs_set_boolean(PREF_OCCUPANTS_OFFLINE, FALSE);
4451 } else {
4452 cons_show("Occupant list disabled.");
4453 prefs_set_boolean(PREF_OCCUPANTS, FALSE);
4454 }
4455 return TRUE;
4456 } else {
4457 cons_bad_cmd_usage(command);
4458 return TRUE;
4459 }
4460 }
4461
4462 // header settings
4463 if (g_strcmp0(args[0], "header") == 0) {
4464 if (g_strcmp0(args[1], "char") == 0) {
4465 if (!args[2]) {
4466 cons_bad_cmd_usage(command);
4467 } else if (g_strcmp0(args[2], "none") == 0) {
4468 prefs_clear_occupants_header_char();
4469 cons_show("Occupants header char removed.");
4470
4471 occupantswin_occupants_all();
4472 } else {
4473 prefs_set_occupants_header_char(args[2][0]);
4474 cons_show("Occupants header char set to %c.", args[2][0]);
4475
4476 occupantswin_occupants_all();
4477 }
4478 } else {
4479 cons_bad_cmd_usage(command);
4480 }
4481 return TRUE;
4482 }
4483
4484 jabber_conn_status_t conn_status = connection_get_status();
4485 if (conn_status != JABBER_CONNECTED) {
4486 cons_show("You are not currently connected.");
4487 return TRUE;
4488 }
4489
4490 if (window->type != WIN_MUC) {
4491 cons_show("Cannot apply setting when not in chat room.");
4492 return TRUE;
4493 }
4494
4495 ProfMucWin* mucwin = (ProfMucWin*)window;
4496 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4497
4498 if (g_strcmp0(args[0], "show") == 0) {
4499 if (g_strcmp0(args[1], "jid") == 0) {
4500 mucwin->showjid = TRUE;
4501 mucwin_update_occupants(mucwin);
4502 } else if (g_strcmp0(args[1], "offline") == 0) {
4503 mucwin->showoffline = TRUE;
4504 mucwin_update_occupants(mucwin);
4505 } else {
4506 mucwin_show_occupants(mucwin);
4507 }
4508 } else if (g_strcmp0(args[0], "hide") == 0) {
4509 if (g_strcmp0(args[1], "jid") == 0) {
4510 mucwin->showjid = FALSE;
4511 mucwin_update_occupants(mucwin);
4512 } else if (g_strcmp0(args[1], "offline") == 0) {
4513 mucwin->showoffline = FALSE;
4514 mucwin_update_occupants(mucwin);
4515 } else {
4516 mucwin_hide_occupants(mucwin);
4517 }
4518 } else {
4519 cons_bad_cmd_usage(command);
4520 }
4521
4522 return TRUE;
4523 }
4524
4525 gboolean
cmd_rooms(ProfWin * window,const char * const command,gchar ** args)4526 cmd_rooms(ProfWin* window, const char* const command, gchar** args)
4527 {
4528 jabber_conn_status_t conn_status = connection_get_status();
4529
4530 if (conn_status != JABBER_CONNECTED) {
4531 cons_show("You are not currently connected.");
4532 return TRUE;
4533 }
4534
4535 gchar* service = NULL;
4536 gchar* filter = NULL;
4537 if (args[0] != NULL) {
4538 if (g_strcmp0(args[0], "service") == 0) {
4539 if (args[1] == NULL) {
4540 cons_bad_cmd_usage(command);
4541 cons_show("");
4542 return TRUE;
4543 }
4544 service = g_strdup(args[1]);
4545 } else if (g_strcmp0(args[0], "filter") == 0) {
4546 if (args[1] == NULL) {
4547 cons_bad_cmd_usage(command);
4548 cons_show("");
4549 return TRUE;
4550 }
4551 filter = g_strdup(args[1]);
4552 } else if (g_strcmp0(args[0], "cache") == 0) {
4553 if (g_strv_length(args) != 2) {
4554 cons_bad_cmd_usage(command);
4555 cons_show("");
4556 return TRUE;
4557 } else if (g_strcmp0(args[1], "on") == 0) {
4558 prefs_set_boolean(PREF_ROOM_LIST_CACHE, TRUE);
4559 cons_show("Rooms list cache enabled.");
4560 return TRUE;
4561 } else if (g_strcmp0(args[1], "off") == 0) {
4562 prefs_set_boolean(PREF_ROOM_LIST_CACHE, FALSE);
4563 cons_show("Rooms list cache disabled.");
4564 return TRUE;
4565 } else if (g_strcmp0(args[1], "clear") == 0) {
4566 iq_rooms_cache_clear();
4567 cons_show("Rooms list cache cleared.");
4568 return TRUE;
4569 } else {
4570 cons_bad_cmd_usage(command);
4571 cons_show("");
4572 return TRUE;
4573 }
4574 } else {
4575 cons_bad_cmd_usage(command);
4576 cons_show("");
4577 return TRUE;
4578 }
4579 }
4580 if (g_strv_length(args) >= 3) {
4581 if (g_strcmp0(args[2], "service") == 0) {
4582 if (args[3] == NULL) {
4583 cons_bad_cmd_usage(command);
4584 cons_show("");
4585 g_free(service);
4586 g_free(filter);
4587 return TRUE;
4588 }
4589 g_free(service);
4590 service = g_strdup(args[3]);
4591 } else if (g_strcmp0(args[2], "filter") == 0) {
4592 if (args[3] == NULL) {
4593 cons_bad_cmd_usage(command);
4594 cons_show("");
4595 g_free(service);
4596 g_free(filter);
4597 return TRUE;
4598 }
4599 g_free(filter);
4600 filter = g_strdup(args[3]);
4601 } else {
4602 cons_bad_cmd_usage(command);
4603 cons_show("");
4604 g_free(service);
4605 g_free(filter);
4606 return TRUE;
4607 }
4608 }
4609
4610 if (service == NULL) {
4611 ProfAccount* account = accounts_get_account(session_get_account_name());
4612 if (account->muc_service) {
4613 service = g_strdup(account->muc_service);
4614 account_free(account);
4615 } else {
4616 cons_show("Account MUC service property not found.");
4617 account_free(account);
4618 g_free(service);
4619 g_free(filter);
4620 return TRUE;
4621 }
4622 }
4623
4624 cons_show("");
4625 if (filter) {
4626 cons_show("Room list request sent: %s, filter: '%s'", service, filter);
4627 } else {
4628 cons_show("Room list request sent: %s", service);
4629 }
4630 iq_room_list_request(service, filter);
4631
4632 g_free(service);
4633 g_free(filter);
4634
4635 return TRUE;
4636 }
4637
4638 gboolean
cmd_bookmark(ProfWin * window,const char * const command,gchar ** args)4639 cmd_bookmark(ProfWin* window, const char* const command, gchar** args)
4640 {
4641 jabber_conn_status_t conn_status = connection_get_status();
4642
4643 if (conn_status != JABBER_CONNECTED) {
4644 cons_show("You are not currently connected.");
4645 cons_alert(NULL);
4646 return TRUE;
4647 }
4648
4649 int num_args = g_strv_length(args);
4650 gchar* cmd = args[0];
4651 if (window->type == WIN_MUC
4652 && num_args < 2
4653 && (cmd == NULL || g_strcmp0(cmd, "add") == 0)) {
4654 // default to current nickname, password, and autojoin "on"
4655 ProfMucWin* mucwin = (ProfMucWin*)window;
4656 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4657 char* nick = muc_nick(mucwin->roomjid);
4658 char* password = muc_password(mucwin->roomjid);
4659 gboolean added = bookmark_add(mucwin->roomjid, nick, password, "on", NULL);
4660 if (added) {
4661 win_println(window, THEME_DEFAULT, "!", "Bookmark added for %s.", mucwin->roomjid);
4662 } else {
4663 win_println(window, THEME_DEFAULT, "!", "Bookmark already exists for %s.", mucwin->roomjid);
4664 }
4665 return TRUE;
4666 }
4667
4668 if (window->type == WIN_MUC
4669 && num_args < 2
4670 && g_strcmp0(cmd, "remove") == 0) {
4671 ProfMucWin* mucwin = (ProfMucWin*)window;
4672 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4673 gboolean removed = bookmark_remove(mucwin->roomjid);
4674 if (removed) {
4675 win_println(window, THEME_DEFAULT, "!", "Bookmark removed for %s.", mucwin->roomjid);
4676 } else {
4677 win_println(window, THEME_DEFAULT, "!", "Bookmark does not exist for %s.", mucwin->roomjid);
4678 }
4679 return TRUE;
4680 }
4681
4682 if (cmd == NULL) {
4683 cons_bad_cmd_usage(command);
4684 cons_alert(NULL);
4685 return TRUE;
4686 }
4687
4688 if (strcmp(cmd, "invites") == 0) {
4689 if (g_strcmp0(args[1], "on") == 0) {
4690 prefs_set_boolean(PREF_BOOKMARK_INVITE, TRUE);
4691 cons_show("Auto bookmarking accepted invites enabled.");
4692 } else if (g_strcmp0(args[1], "off") == 0) {
4693 prefs_set_boolean(PREF_BOOKMARK_INVITE, FALSE);
4694 cons_show("Auto bookmarking accepted invites disabled.");
4695 } else {
4696 cons_bad_cmd_usage(command);
4697 cons_show("");
4698 }
4699 cons_alert(NULL);
4700 return TRUE;
4701 }
4702
4703 if (strcmp(cmd, "list") == 0) {
4704 char* bookmark_jid = args[1];
4705 if (bookmark_jid == NULL) {
4706 // list all bookmarks
4707 GList* bookmarks = bookmark_get_list();
4708 cons_show_bookmarks(bookmarks);
4709 g_list_free(bookmarks);
4710 } else {
4711 // list one bookmark
4712 Bookmark *bookmark = bookmark_get_by_jid(bookmark_jid);
4713 cons_show_bookmark(bookmark);
4714 }
4715
4716 return TRUE;
4717 }
4718
4719 char* jid = args[1];
4720 if (jid == NULL) {
4721 cons_bad_cmd_usage(command);
4722 cons_show("");
4723 cons_alert(NULL);
4724 return TRUE;
4725 }
4726 if (strchr(jid, '@') == NULL) {
4727 cons_show("Invalid room, must be of the form room@domain.tld");
4728 cons_show("");
4729 cons_alert(NULL);
4730 return TRUE;
4731 }
4732
4733 if (strcmp(cmd, "remove") == 0) {
4734 gboolean removed = bookmark_remove(jid);
4735 if (removed) {
4736 cons_show("Bookmark removed for %s.", jid);
4737 } else {
4738 cons_show("No bookmark exists for %s.", jid);
4739 }
4740 cons_alert(NULL);
4741 return TRUE;
4742 }
4743
4744 if (strcmp(cmd, "join") == 0) {
4745 gboolean joined = bookmark_join(jid);
4746 if (!joined) {
4747 cons_show("No bookmark exists for %s.", jid);
4748 }
4749 cons_alert(NULL);
4750 return TRUE;
4751 }
4752
4753 gchar* opt_keys[] = { "autojoin", "nick", "password", "name", NULL };
4754 gboolean parsed;
4755
4756 GHashTable* options = parse_options(&args[2], opt_keys, &parsed);
4757 if (!parsed) {
4758 cons_bad_cmd_usage(command);
4759 cons_show("");
4760 cons_alert(NULL);
4761 return TRUE;
4762 }
4763
4764 char* autojoin = g_hash_table_lookup(options, "autojoin");
4765
4766 if (autojoin && ((strcmp(autojoin, "on") != 0) && (strcmp(autojoin, "off") != 0))) {
4767 cons_bad_cmd_usage(command);
4768 cons_show("");
4769 options_destroy(options);
4770 cons_alert(NULL);
4771 return TRUE;
4772 }
4773
4774 char* nick = g_hash_table_lookup(options, "nick");
4775 char* password = g_hash_table_lookup(options, "password");
4776 char* name = g_hash_table_lookup(options, "name");
4777
4778 if (strcmp(cmd, "add") == 0) {
4779 gboolean added = bookmark_add(jid, nick, password, autojoin, name);
4780 if (added) {
4781 cons_show("Bookmark added for %s.", jid);
4782 } else {
4783 cons_show("Bookmark already exists, use /bookmark update to edit.");
4784 }
4785 options_destroy(options);
4786 cons_alert(NULL);
4787 return TRUE;
4788 }
4789
4790 if (strcmp(cmd, "update") == 0) {
4791 gboolean updated = bookmark_update(jid, nick, password, autojoin, name);
4792 if (updated) {
4793 cons_show("Bookmark updated.");
4794 } else {
4795 cons_show("No bookmark exists for %s.", jid);
4796 }
4797 options_destroy(options);
4798 cons_alert(NULL);
4799 return TRUE;
4800 }
4801
4802 cons_bad_cmd_usage(command);
4803 options_destroy(options);
4804 cons_alert(NULL);
4805
4806 return TRUE;
4807 }
4808
4809 gboolean
cmd_bookmark_ignore(ProfWin * window,const char * const command,gchar ** args)4810 cmd_bookmark_ignore(ProfWin* window, const char* const command, gchar** args)
4811 {
4812 jabber_conn_status_t conn_status = connection_get_status();
4813
4814 if (conn_status != JABBER_CONNECTED) {
4815 cons_show("You are not currently connected.");
4816 cons_alert(NULL);
4817 return TRUE;
4818 }
4819
4820 // `/bookmark ignore` lists them
4821 if (args[1] == NULL) {
4822 gsize len = 0;
4823 gchar** list = bookmark_ignore_list(&len);
4824 cons_show_bookmarks_ignore(list, len);
4825 g_strfreev(list);
4826 return TRUE;
4827 }
4828
4829 if (strcmp(args[1], "add") == 0 && args[2] != NULL) {
4830 bookmark_ignore_add(args[2]);
4831 cons_show("Autojoin for bookmark %s added to ignore list.", args[2]);
4832 return TRUE;
4833 }
4834
4835 if (strcmp(args[1], "remove") == 0 && args[2] != NULL) {
4836 bookmark_ignore_remove(args[2]);
4837 cons_show("Autojoin for bookmark %s removed from ignore list.", args[2]);
4838 return TRUE;
4839 }
4840
4841 cons_bad_cmd_usage(command);
4842 return TRUE;
4843 }
4844
4845 gboolean
cmd_disco(ProfWin * window,const char * const command,gchar ** args)4846 cmd_disco(ProfWin* window, const char* const command, gchar** args)
4847 {
4848 jabber_conn_status_t conn_status = connection_get_status();
4849
4850 if (conn_status != JABBER_CONNECTED) {
4851 cons_show("You are not currently connected.");
4852 return TRUE;
4853 }
4854
4855 GString* jid = g_string_new("");
4856 if (args[1]) {
4857 jid = g_string_append(jid, args[1]);
4858 } else {
4859 Jid* jidp = jid_create(connection_get_fulljid());
4860 jid = g_string_append(jid, jidp->domainpart);
4861 jid_destroy(jidp);
4862 }
4863
4864 if (g_strcmp0(args[0], "info") == 0) {
4865 iq_disco_info_request(jid->str);
4866 } else {
4867 iq_disco_items_request(jid->str);
4868 }
4869
4870 g_string_free(jid, TRUE);
4871
4872 return TRUE;
4873 }
4874
4875 // TODO: Move this into its own tools such as HTTPUpload or AESGCMDownload.
4876 #ifdef HAVE_OMEMO
4877 char*
_add_omemo_stream(int * fd,FILE ** fh,char ** err)4878 _add_omemo_stream(int* fd, FILE** fh, char** err)
4879 {
4880 // Create temporary file for writing ciphertext.
4881 int tmpfd;
4882 char* tmpname = NULL;
4883 if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) {
4884 *err = "Unable to create temporary file for encrypted transfer.";
4885 return NULL;
4886 }
4887 FILE* tmpfh = fdopen(tmpfd, "wb");
4888
4889 // The temporary ciphertext file should be removed after it has
4890 // been closed.
4891 remove(tmpname);
4892 free(tmpname);
4893
4894 int crypt_res;
4895 char* fragment;
4896 fragment = omemo_encrypt_file(*fh, tmpfh, file_size(*fd), &crypt_res);
4897 if (crypt_res != 0) {
4898 fclose(tmpfh);
4899 return NULL;
4900 }
4901
4902 // Force flush as the upload will read from the same stream.
4903 fflush(tmpfh);
4904 rewind(tmpfh);
4905
4906 fclose(*fh); // Also closes descriptor.
4907
4908 // Switch original stream with temporary ciphertext stream.
4909 *fd = tmpfd;
4910 *fh = tmpfh;
4911
4912 return fragment;
4913 }
4914 #endif
4915
4916 gboolean
cmd_sendfile(ProfWin * window,const char * const command,gchar ** args)4917 cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
4918 {
4919 jabber_conn_status_t conn_status = connection_get_status();
4920 gchar* filename;
4921 char* alt_scheme = NULL;
4922 char* alt_fragment = NULL;
4923
4924 // expand ~ to $HOME
4925 filename = get_expanded_path(args[0]);
4926
4927 if (access(filename, R_OK) != 0) {
4928 cons_show_error("Uploading '%s' failed: File not found!", filename);
4929 goto out;
4930 }
4931
4932 if (!is_regular_file(filename)) {
4933 cons_show_error("Uploading '%s' failed: Not a file!", filename);
4934 goto out;
4935 }
4936
4937 if (conn_status != JABBER_CONNECTED) {
4938 cons_show("You are not currently connected.");
4939 goto out;
4940 }
4941
4942 if (window->type != WIN_CHAT && window->type != WIN_PRIVATE && window->type != WIN_MUC) {
4943 cons_show_error("Unsupported window for file transmission.");
4944 goto out;
4945 }
4946
4947 int fd;
4948 if ((fd = open(filename, O_RDONLY)) == -1) {
4949 cons_show_error("Unable to open file descriptor for '%s'.", filename);
4950 goto out;
4951 }
4952
4953 FILE* fh = fdopen(fd, "rb");
4954
4955 gboolean omemo_enabled = FALSE;
4956 gboolean sendfile_enabled = TRUE;
4957
4958 switch (window->type) {
4959 case WIN_MUC:
4960 {
4961 ProfMucWin* mucwin = (ProfMucWin*)window;
4962 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
4963 omemo_enabled = mucwin->is_omemo == TRUE;
4964 break;
4965 }
4966 case WIN_CHAT:
4967 {
4968 ProfChatWin* chatwin = (ProfChatWin*)window;
4969 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
4970 omemo_enabled = chatwin->is_omemo == TRUE;
4971 sendfile_enabled = !((chatwin->pgp_send == TRUE && !prefs_get_boolean(PREF_PGP_SENDFILE))
4972 || (chatwin->is_otr == TRUE && !prefs_get_boolean(PREF_OTR_SENDFILE)));
4973 break;
4974 }
4975
4976 case WIN_PRIVATE: // We don't support encryption in private MUC windows.
4977 default:
4978 cons_show_error("Unsupported window for file transmission.");
4979 goto out;
4980 }
4981
4982 if (!sendfile_enabled) {
4983 cons_show_error("Uploading unencrypted files disabled. See /otr sendfile or /pgp sendfile.");
4984 win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
4985 goto out;
4986 }
4987
4988 if (omemo_enabled) {
4989 #ifdef HAVE_OMEMO
4990 char* err = NULL;
4991 alt_scheme = OMEMO_AESGCM_URL_SCHEME;
4992 alt_fragment = _add_omemo_stream(&fd, &fh, &err);
4993 if (err != NULL) {
4994 cons_show_error(err);
4995 win_println(window, THEME_ERROR, "-", err);
4996 goto out;
4997 }
4998 #endif
4999 }
5000
5001 HTTPUpload* upload = malloc(sizeof(HTTPUpload));
5002 upload->window = window;
5003
5004 upload->filename = strdup(filename);
5005 upload->filehandle = fh;
5006 upload->filesize = file_size(fd);
5007 upload->mime_type = file_mime_type(filename);
5008
5009 if (alt_scheme != NULL) {
5010 upload->alt_scheme = strdup(alt_scheme);
5011 } else {
5012 upload->alt_scheme = NULL;
5013 }
5014
5015 if (alt_fragment != NULL) {
5016 upload->alt_fragment = strdup(alt_fragment);
5017 } else {
5018 upload->alt_fragment = NULL;
5019 }
5020
5021 iq_http_upload_request(upload);
5022
5023 out:
5024 #ifdef HAVE_OMEMO
5025 if (alt_fragment != NULL)
5026 omemo_free(alt_fragment);
5027 #endif
5028 if (filename != NULL)
5029 free(filename);
5030
5031 return TRUE;
5032 }
5033
5034 gboolean
cmd_lastactivity(ProfWin * window,const char * const command,gchar ** args)5035 cmd_lastactivity(ProfWin* window, const char* const command, gchar** args)
5036 {
5037 if ((g_strcmp0(args[0], "set") == 0)) {
5038 if ((g_strcmp0(args[1], "on") == 0) || (g_strcmp0(args[1], "off") == 0)) {
5039 _cmd_set_boolean_preference(args[1], command, "Last activity", PREF_LASTACTIVITY);
5040 if (g_strcmp0(args[1], "on") == 0) {
5041 caps_add_feature(XMPP_FEATURE_LASTACTIVITY);
5042 }
5043 if (g_strcmp0(args[1], "off") == 0) {
5044 caps_remove_feature(XMPP_FEATURE_LASTACTIVITY);
5045 }
5046 return TRUE;
5047 } else {
5048 cons_bad_cmd_usage(command);
5049 return TRUE;
5050 }
5051 }
5052
5053 jabber_conn_status_t conn_status = connection_get_status();
5054
5055 if (conn_status != JABBER_CONNECTED) {
5056 cons_show("You are not currently connected.");
5057 return TRUE;
5058 }
5059
5060 if ((g_strcmp0(args[0], "get") == 0)) {
5061 if (args[1] == NULL) {
5062 Jid* jidp = jid_create(connection_get_fulljid());
5063 GString* jid = g_string_new(jidp->domainpart);
5064
5065 iq_last_activity_request(jid->str);
5066
5067 g_string_free(jid, TRUE);
5068 jid_destroy(jidp);
5069
5070 return TRUE;
5071 } else {
5072 iq_last_activity_request(args[1]);
5073 return TRUE;
5074 }
5075 }
5076
5077 cons_bad_cmd_usage(command);
5078 return TRUE;
5079 }
5080
5081 gboolean
cmd_nick(ProfWin * window,const char * const command,gchar ** args)5082 cmd_nick(ProfWin* window, const char* const command, gchar** args)
5083 {
5084 jabber_conn_status_t conn_status = connection_get_status();
5085
5086 if (conn_status != JABBER_CONNECTED) {
5087 cons_show("You are not currently connected.");
5088 return TRUE;
5089 }
5090 if (window->type != WIN_MUC) {
5091 cons_show("You can only change your nickname in a chat room window.");
5092 return TRUE;
5093 }
5094
5095 ProfMucWin* mucwin = (ProfMucWin*)window;
5096 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
5097 char* nick = args[0];
5098 presence_change_room_nick(mucwin->roomjid, nick);
5099
5100 return TRUE;
5101 }
5102
5103 gboolean
cmd_alias(ProfWin * window,const char * const command,gchar ** args)5104 cmd_alias(ProfWin* window, const char* const command, gchar** args)
5105 {
5106 char* subcmd = args[0];
5107
5108 if (strcmp(subcmd, "add") == 0) {
5109 char* alias = args[1];
5110 if (alias == NULL) {
5111 cons_bad_cmd_usage(command);
5112 return TRUE;
5113 } else {
5114 char* alias_p = alias;
5115 GString* ac_value = g_string_new("");
5116 if (alias[0] == '/') {
5117 g_string_append(ac_value, alias);
5118 alias_p = &alias[1];
5119 } else {
5120 g_string_append(ac_value, "/");
5121 g_string_append(ac_value, alias);
5122 }
5123
5124 char* value = args[2];
5125 if (value == NULL) {
5126 cons_bad_cmd_usage(command);
5127 g_string_free(ac_value, TRUE);
5128 return TRUE;
5129 } else if (cmd_ac_exists(ac_value->str)) {
5130 cons_show("Command or alias '%s' already exists.", ac_value->str);
5131 g_string_free(ac_value, TRUE);
5132 return TRUE;
5133 } else {
5134 prefs_add_alias(alias_p, value);
5135 cmd_ac_add(ac_value->str);
5136 cmd_ac_add_alias_value(alias_p);
5137 cons_show("Command alias added %s -> %s", ac_value->str, value);
5138 g_string_free(ac_value, TRUE);
5139 return TRUE;
5140 }
5141 }
5142 } else if (strcmp(subcmd, "remove") == 0) {
5143 char* alias = args[1];
5144 if (alias == NULL) {
5145 cons_bad_cmd_usage(command);
5146 return TRUE;
5147 } else {
5148 if (alias[0] == '/') {
5149 alias = &alias[1];
5150 }
5151 gboolean removed = prefs_remove_alias(alias);
5152 if (!removed) {
5153 cons_show("No such command alias /%s", alias);
5154 } else {
5155 GString* ac_value = g_string_new("/");
5156 g_string_append(ac_value, alias);
5157 cmd_ac_remove(ac_value->str);
5158 cmd_ac_remove_alias_value(alias);
5159 g_string_free(ac_value, TRUE);
5160 cons_show("Command alias removed -> /%s", alias);
5161 }
5162 return TRUE;
5163 }
5164 } else if (strcmp(subcmd, "list") == 0) {
5165 GList* aliases = prefs_get_aliases();
5166 cons_show_aliases(aliases);
5167 prefs_free_aliases(aliases);
5168 return TRUE;
5169 } else {
5170 cons_bad_cmd_usage(command);
5171 return TRUE;
5172 }
5173 }
5174
5175 gboolean
cmd_clear(ProfWin * window,const char * const command,gchar ** args)5176 cmd_clear(ProfWin* window, const char* const command, gchar** args)
5177 {
5178 if (args[0] == NULL) {
5179 win_clear(window);
5180 return TRUE;
5181 } else {
5182 if ((g_strcmp0(args[0], "persist_history") == 0)) {
5183
5184 if (args[1] != NULL) {
5185 if ((g_strcmp0(args[1], "on") == 0) || (g_strcmp0(args[1], "off") == 0)) {
5186 _cmd_set_boolean_preference(args[1], command, "Persistent history", PREF_CLEAR_PERSIST_HISTORY);
5187 return TRUE;
5188 }
5189 } else {
5190 if (prefs_get_boolean(PREF_CLEAR_PERSIST_HISTORY)) {
5191 win_println(window, THEME_DEFAULT, "!", " Persistently clear screen : ON");
5192 } else {
5193 win_println(window, THEME_DEFAULT, "!", " Persistently clear screen : OFF");
5194 }
5195 return TRUE;
5196 }
5197 }
5198 }
5199 cons_bad_cmd_usage(command);
5200
5201 return TRUE;
5202 }
5203
5204 gboolean
cmd_privileges(ProfWin * window,const char * const command,gchar ** args)5205 cmd_privileges(ProfWin* window, const char* const command, gchar** args)
5206 {
5207 _cmd_set_boolean_preference(args[0], command, "MUC privileges", PREF_MUC_PRIVILEGES);
5208
5209 ui_redraw_all_room_rosters();
5210
5211 return TRUE;
5212 }
5213
5214 gboolean
cmd_charset(ProfWin * window,const char * const command,gchar ** args)5215 cmd_charset(ProfWin* window, const char* const command, gchar** args)
5216 {
5217 char* codeset = nl_langinfo(CODESET);
5218 char* lang = getenv("LANG");
5219
5220 cons_show("Charset information:");
5221
5222 if (lang) {
5223 cons_show(" LANG: %s", lang);
5224 }
5225 if (codeset) {
5226 cons_show(" CODESET: %s", codeset);
5227 }
5228 cons_show(" MB_CUR_MAX: %d", MB_CUR_MAX);
5229 cons_show(" MB_LEN_MAX: %d", MB_LEN_MAX);
5230
5231 return TRUE;
5232 }
5233
5234 gboolean
cmd_beep(ProfWin * window,const char * const command,gchar ** args)5235 cmd_beep(ProfWin* window, const char* const command, gchar** args)
5236 {
5237 _cmd_set_boolean_preference(args[0], command, "Sound", PREF_BEEP);
5238 return TRUE;
5239 }
5240
5241 gboolean
cmd_console(ProfWin * window,const char * const command,gchar ** args)5242 cmd_console(ProfWin* window, const char* const command, gchar** args)
5243 {
5244 gboolean isMuc = (g_strcmp0(args[0], "muc") == 0);
5245
5246 if ((g_strcmp0(args[0], "chat") != 0) && !isMuc && (g_strcmp0(args[0], "private") != 0)) {
5247 cons_bad_cmd_usage(command);
5248 return TRUE;
5249 }
5250
5251 gchar* setting = args[1];
5252 if ((g_strcmp0(setting, "all") != 0) && (g_strcmp0(setting, "first") != 0) && (g_strcmp0(setting, "none") != 0)) {
5253 if (!(isMuc && (g_strcmp0(setting, "mention") == 0))) {
5254 cons_bad_cmd_usage(command);
5255 return TRUE;
5256 }
5257 }
5258
5259 if (g_strcmp0(args[0], "chat") == 0) {
5260 prefs_set_string(PREF_CONSOLE_CHAT, setting);
5261 cons_show("Console chat messages set: %s", setting);
5262 return TRUE;
5263 }
5264
5265 if (g_strcmp0(args[0], "muc") == 0) {
5266 prefs_set_string(PREF_CONSOLE_MUC, setting);
5267 cons_show("Console MUC messages set: %s", setting);
5268 return TRUE;
5269 }
5270
5271 if (g_strcmp0(args[0], "private") == 0) {
5272 prefs_set_string(PREF_CONSOLE_PRIVATE, setting);
5273 cons_show("Console private room messages set: %s", setting);
5274 return TRUE;
5275 }
5276
5277 return TRUE;
5278 }
5279
5280 gboolean
cmd_presence(ProfWin * window,const char * const command,gchar ** args)5281 cmd_presence(ProfWin* window, const char* const command, gchar** args)
5282 {
5283 if (strcmp(args[0], "console") != 0 && strcmp(args[0], "chat") != 0 && strcmp(args[0], "room") != 0 && strcmp(args[0], "titlebar") != 0) {
5284 cons_bad_cmd_usage(command);
5285 return TRUE;
5286 }
5287
5288 if (strcmp(args[0], "titlebar") == 0) {
5289 _cmd_set_boolean_preference(args[1], command, "Contact presence", PREF_PRESENCE);
5290 return TRUE;
5291 }
5292
5293 if (strcmp(args[1], "all") != 0 && strcmp(args[1], "online") != 0 && strcmp(args[1], "none") != 0) {
5294 cons_bad_cmd_usage(command);
5295 return TRUE;
5296 }
5297
5298 if (strcmp(args[0], "console") == 0) {
5299 prefs_set_string(PREF_STATUSES_CONSOLE, args[1]);
5300 if (strcmp(args[1], "all") == 0) {
5301 cons_show("All presence updates will appear in the console.");
5302 } else if (strcmp(args[1], "online") == 0) {
5303 cons_show("Only online/offline presence updates will appear in the console.");
5304 } else {
5305 cons_show("Presence updates will not appear in the console.");
5306 }
5307 }
5308
5309 if (strcmp(args[0], "chat") == 0) {
5310 prefs_set_string(PREF_STATUSES_CHAT, args[1]);
5311 if (strcmp(args[1], "all") == 0) {
5312 cons_show("All presence updates will appear in chat windows.");
5313 } else if (strcmp(args[1], "online") == 0) {
5314 cons_show("Only online/offline presence updates will appear in chat windows.");
5315 } else {
5316 cons_show("Presence updates will not appear in chat windows.");
5317 }
5318 }
5319
5320 if (strcmp(args[0], "room") == 0) {
5321 prefs_set_string(PREF_STATUSES_MUC, args[1]);
5322 if (strcmp(args[1], "all") == 0) {
5323 cons_show("All presence updates will appear in chat room windows.");
5324 } else if (strcmp(args[1], "online") == 0) {
5325 cons_show("Only join/leave presence updates will appear in chat room windows.");
5326 } else {
5327 cons_show("Presence updates will not appear in chat room windows.");
5328 }
5329 }
5330
5331 return TRUE;
5332 }
5333
5334 gboolean
cmd_wrap(ProfWin * window,const char * const command,gchar ** args)5335 cmd_wrap(ProfWin* window, const char* const command, gchar** args)
5336 {
5337 _cmd_set_boolean_preference(args[0], command, "Word wrap", PREF_WRAP);
5338
5339 wins_resize_all();
5340
5341 return TRUE;
5342 }
5343
5344 gboolean
cmd_time(ProfWin * window,const char * const command,gchar ** args)5345 cmd_time(ProfWin* window, const char* const command, gchar** args)
5346 {
5347 if (g_strcmp0(args[0], "lastactivity") == 0) {
5348 if (args[1] == NULL) {
5349 char* format = prefs_get_string(PREF_TIME_LASTACTIVITY);
5350 cons_show("Last activity time format: '%s'.", format);
5351 g_free(format);
5352 return TRUE;
5353 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5354 prefs_set_string(PREF_TIME_LASTACTIVITY, args[2]);
5355 cons_show("Last activity time format set to '%s'.", args[2]);
5356 ui_redraw();
5357 return TRUE;
5358 } else if (g_strcmp0(args[1], "off") == 0) {
5359 cons_show("Last activity time cannot be disabled.");
5360 ui_redraw();
5361 return TRUE;
5362 } else {
5363 cons_bad_cmd_usage(command);
5364 return TRUE;
5365 }
5366 } else if (g_strcmp0(args[0], "statusbar") == 0) {
5367 if (args[1] == NULL) {
5368 char* format = prefs_get_string(PREF_TIME_STATUSBAR);
5369 cons_show("Status bar time format: '%s'.", format);
5370 g_free(format);
5371 return TRUE;
5372 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5373 prefs_set_string(PREF_TIME_STATUSBAR, args[2]);
5374 cons_show("Status bar time format set to '%s'.", args[2]);
5375 ui_redraw();
5376 return TRUE;
5377 } else if (g_strcmp0(args[1], "off") == 0) {
5378 prefs_set_string(PREF_TIME_STATUSBAR, "off");
5379 cons_show("Status bar time display disabled.");
5380 ui_redraw();
5381 return TRUE;
5382 } else {
5383 cons_bad_cmd_usage(command);
5384 return TRUE;
5385 }
5386 } else if (g_strcmp0(args[0], "console") == 0) {
5387 if (args[1] == NULL) {
5388 char* format = prefs_get_string(PREF_TIME_CONSOLE);
5389 cons_show("Console time format: '%s'.", format);
5390 g_free(format);
5391 return TRUE;
5392 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5393 prefs_set_string(PREF_TIME_CONSOLE, args[2]);
5394 cons_show("Console time format set to '%s'.", args[2]);
5395 wins_resize_all();
5396 return TRUE;
5397 } else if (g_strcmp0(args[1], "off") == 0) {
5398 prefs_set_string(PREF_TIME_CONSOLE, "off");
5399 cons_show("Console time display disabled.");
5400 wins_resize_all();
5401 return TRUE;
5402 } else {
5403 cons_bad_cmd_usage(command);
5404 return TRUE;
5405 }
5406 } else if (g_strcmp0(args[0], "chat") == 0) {
5407 if (args[1] == NULL) {
5408 char* format = prefs_get_string(PREF_TIME_CHAT);
5409 cons_show("Chat time format: '%s'.", format);
5410 g_free(format);
5411 return TRUE;
5412 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5413 prefs_set_string(PREF_TIME_CHAT, args[2]);
5414 cons_show("Chat time format set to '%s'.", args[2]);
5415 wins_resize_all();
5416 return TRUE;
5417 } else if (g_strcmp0(args[1], "off") == 0) {
5418 prefs_set_string(PREF_TIME_CHAT, "off");
5419 cons_show("Chat time display disabled.");
5420 wins_resize_all();
5421 return TRUE;
5422 } else {
5423 cons_bad_cmd_usage(command);
5424 return TRUE;
5425 }
5426 } else if (g_strcmp0(args[0], "muc") == 0) {
5427 if (args[1] == NULL) {
5428 char* format = prefs_get_string(PREF_TIME_MUC);
5429 cons_show("MUC time format: '%s'.", format);
5430 g_free(format);
5431 return TRUE;
5432 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5433 prefs_set_string(PREF_TIME_MUC, args[2]);
5434 cons_show("MUC time format set to '%s'.", args[2]);
5435 wins_resize_all();
5436 return TRUE;
5437 } else if (g_strcmp0(args[1], "off") == 0) {
5438 prefs_set_string(PREF_TIME_MUC, "off");
5439 cons_show("MUC time display disabled.");
5440 wins_resize_all();
5441 return TRUE;
5442 } else {
5443 cons_bad_cmd_usage(command);
5444 return TRUE;
5445 }
5446 } else if (g_strcmp0(args[0], "config") == 0) {
5447 if (args[1] == NULL) {
5448 char* format = prefs_get_string(PREF_TIME_CONFIG);
5449 cons_show("config time format: '%s'.", format);
5450 g_free(format);
5451 return TRUE;
5452 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5453 prefs_set_string(PREF_TIME_CONFIG, args[2]);
5454 cons_show("config time format set to '%s'.", args[2]);
5455 wins_resize_all();
5456 return TRUE;
5457 } else if (g_strcmp0(args[1], "off") == 0) {
5458 prefs_set_string(PREF_TIME_CONFIG, "off");
5459 cons_show("config time display disabled.");
5460 wins_resize_all();
5461 return TRUE;
5462 } else {
5463 cons_bad_cmd_usage(command);
5464 return TRUE;
5465 }
5466 } else if (g_strcmp0(args[0], "private") == 0) {
5467 if (args[1] == NULL) {
5468 char* format = prefs_get_string(PREF_TIME_PRIVATE);
5469 cons_show("Private chat time format: '%s'.", format);
5470 g_free(format);
5471 return TRUE;
5472 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5473 prefs_set_string(PREF_TIME_PRIVATE, args[2]);
5474 cons_show("Private chat time format set to '%s'.", args[2]);
5475 wins_resize_all();
5476 return TRUE;
5477 } else if (g_strcmp0(args[1], "off") == 0) {
5478 prefs_set_string(PREF_TIME_PRIVATE, "off");
5479 cons_show("Private chat time display disabled.");
5480 wins_resize_all();
5481 return TRUE;
5482 } else {
5483 cons_bad_cmd_usage(command);
5484 return TRUE;
5485 }
5486 } else if (g_strcmp0(args[0], "xml") == 0) {
5487 if (args[1] == NULL) {
5488 char* format = prefs_get_string(PREF_TIME_XMLCONSOLE);
5489 cons_show("XML Console time format: '%s'.", format);
5490 g_free(format);
5491 return TRUE;
5492 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5493 prefs_set_string(PREF_TIME_XMLCONSOLE, args[2]);
5494 cons_show("XML Console time format set to '%s'.", args[2]);
5495 wins_resize_all();
5496 return TRUE;
5497 } else if (g_strcmp0(args[1], "off") == 0) {
5498 prefs_set_string(PREF_TIME_XMLCONSOLE, "off");
5499 cons_show("XML Console time display disabled.");
5500 wins_resize_all();
5501 return TRUE;
5502 } else {
5503 cons_bad_cmd_usage(command);
5504 return TRUE;
5505 }
5506 } else if (g_strcmp0(args[0], "all") == 0) {
5507 if (args[1] == NULL) {
5508 cons_time_setting();
5509 return TRUE;
5510 } else if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
5511 prefs_set_string(PREF_TIME_CONSOLE, args[2]);
5512 cons_show("Console time format set to '%s'.", args[2]);
5513 prefs_set_string(PREF_TIME_CHAT, args[2]);
5514 cons_show("Chat time format set to '%s'.", args[2]);
5515 prefs_set_string(PREF_TIME_MUC, args[2]);
5516 cons_show("MUC time format set to '%s'.", args[2]);
5517 prefs_set_string(PREF_TIME_CONFIG, args[2]);
5518 cons_show("config time format set to '%s'.", args[2]);
5519 prefs_set_string(PREF_TIME_PRIVATE, args[2]);
5520 cons_show("Private chat time format set to '%s'.", args[2]);
5521 prefs_set_string(PREF_TIME_XMLCONSOLE, args[2]);
5522 cons_show("XML Console time format set to '%s'.", args[2]);
5523 wins_resize_all();
5524 return TRUE;
5525 } else if (g_strcmp0(args[1], "off") == 0) {
5526 prefs_set_string(PREF_TIME_CONSOLE, "off");
5527 cons_show("Console time display disabled.");
5528 prefs_set_string(PREF_TIME_CHAT, "off");
5529 cons_show("Chat time display disabled.");
5530 prefs_set_string(PREF_TIME_MUC, "off");
5531 cons_show("MUC time display disabled.");
5532 prefs_set_string(PREF_TIME_CONFIG, "off");
5533 cons_show("config time display disabled.");
5534 prefs_set_string(PREF_TIME_PRIVATE, "off");
5535 cons_show("config time display disabled.");
5536 prefs_set_string(PREF_TIME_XMLCONSOLE, "off");
5537 cons_show("XML Console time display disabled.");
5538 ui_redraw();
5539 return TRUE;
5540 } else {
5541 cons_bad_cmd_usage(command);
5542 return TRUE;
5543 }
5544 } else {
5545 cons_bad_cmd_usage(command);
5546 return TRUE;
5547 }
5548 }
5549
5550 gboolean
cmd_states(ProfWin * window,const char * const command,gchar ** args)5551 cmd_states(ProfWin* window, const char* const command, gchar** args)
5552 {
5553 if (args[0] == NULL) {
5554 return FALSE;
5555 }
5556
5557 _cmd_set_boolean_preference(args[0], command, "Sending chat states", PREF_STATES);
5558
5559 // if disabled, disable outtype and gone
5560 if (strcmp(args[0], "off") == 0) {
5561 prefs_set_boolean(PREF_OUTTYPE, FALSE);
5562 prefs_set_gone(0);
5563 }
5564
5565 return TRUE;
5566 }
5567
5568 gboolean
cmd_wintitle(ProfWin * window,const char * const command,gchar ** args)5569 cmd_wintitle(ProfWin* window, const char* const command, gchar** args)
5570 {
5571 if (g_strcmp0(args[0], "show") != 0 && g_strcmp0(args[0], "goodbye") != 0) {
5572 cons_bad_cmd_usage(command);
5573 return TRUE;
5574 }
5575 if (g_strcmp0(args[0], "show") == 0 && g_strcmp0(args[1], "off") == 0) {
5576 ui_clear_win_title();
5577 }
5578 if (g_strcmp0(args[0], "show") == 0) {
5579 _cmd_set_boolean_preference(args[1], command, "Window title show", PREF_WINTITLE_SHOW);
5580 } else {
5581 _cmd_set_boolean_preference(args[1], command, "Window title goodbye", PREF_WINTITLE_GOODBYE);
5582 }
5583
5584 return TRUE;
5585 }
5586
5587 gboolean
cmd_outtype(ProfWin * window,const char * const command,gchar ** args)5588 cmd_outtype(ProfWin* window, const char* const command, gchar** args)
5589 {
5590 if (args[0] == NULL) {
5591 return FALSE;
5592 }
5593
5594 _cmd_set_boolean_preference(args[0], command, "Sending typing notifications", PREF_OUTTYPE);
5595
5596 // if enabled, enable states
5597 if (strcmp(args[0], "on") == 0) {
5598 prefs_set_boolean(PREF_STATES, TRUE);
5599 }
5600
5601 return TRUE;
5602 }
5603
5604 gboolean
cmd_gone(ProfWin * window,const char * const command,gchar ** args)5605 cmd_gone(ProfWin* window, const char* const command, gchar** args)
5606 {
5607 char* value = args[0];
5608
5609 gint period = atoi(value);
5610 prefs_set_gone(period);
5611 if (period == 0) {
5612 cons_show("Automatic leaving conversations after period disabled.");
5613 } else if (period == 1) {
5614 cons_show("Leaving conversations after 1 minute of inactivity.");
5615 } else {
5616 cons_show("Leaving conversations after %d minutes of inactivity.", period);
5617 }
5618
5619 // if enabled, enable states
5620 if (period > 0) {
5621 prefs_set_boolean(PREF_STATES, TRUE);
5622 }
5623
5624 return TRUE;
5625 }
5626
5627 gboolean
cmd_notify(ProfWin * window,const char * const command,gchar ** args)5628 cmd_notify(ProfWin* window, const char* const command, gchar** args)
5629 {
5630 if (!args[0]) {
5631 ProfWin* current = wins_get_current();
5632 if (current->type == WIN_MUC) {
5633 win_println(current, THEME_DEFAULT, "-", "");
5634 ProfMucWin* mucwin = (ProfMucWin*)current;
5635
5636 win_println(window, THEME_DEFAULT, "!", "Notification settings for %s:", mucwin->roomjid);
5637 if (prefs_has_room_notify(mucwin->roomjid)) {
5638 if (prefs_get_room_notify(mucwin->roomjid)) {
5639 win_println(window, THEME_DEFAULT, "!", " Message : ON");
5640 } else {
5641 win_println(window, THEME_DEFAULT, "!", " Message : OFF");
5642 }
5643 } else {
5644 if (prefs_get_boolean(PREF_NOTIFY_ROOM)) {
5645 win_println(window, THEME_DEFAULT, "!", " Message : ON (global setting)");
5646 } else {
5647 win_println(window, THEME_DEFAULT, "!", " Message : OFF (global setting)");
5648 }
5649 }
5650 if (prefs_has_room_notify_mention(mucwin->roomjid)) {
5651 if (prefs_get_room_notify_mention(mucwin->roomjid)) {
5652 win_println(window, THEME_DEFAULT, "!", " Mention : ON");
5653 } else {
5654 win_println(window, THEME_DEFAULT, "!", " Mention : OFF");
5655 }
5656 } else {
5657 if (prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION)) {
5658 win_println(window, THEME_DEFAULT, "!", " Mention : ON (global setting)");
5659 } else {
5660 win_println(window, THEME_DEFAULT, "!", " Mention : OFF (global setting)");
5661 }
5662 }
5663 if (prefs_has_room_notify_trigger(mucwin->roomjid)) {
5664 if (prefs_get_room_notify_trigger(mucwin->roomjid)) {
5665 win_println(window, THEME_DEFAULT, "!", " Triggers : ON");
5666 } else {
5667 win_println(window, THEME_DEFAULT, "!", " Triggers : OFF");
5668 }
5669 } else {
5670 if (prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER)) {
5671 win_println(window, THEME_DEFAULT, "!", " Triggers : ON (global setting)");
5672 } else {
5673 win_println(window, THEME_DEFAULT, "!", " Triggers : OFF (global setting)");
5674 }
5675 }
5676 win_println(current, THEME_DEFAULT, "-", "");
5677 } else {
5678 cons_show("");
5679 cons_notify_setting();
5680 cons_bad_cmd_usage(command);
5681 }
5682 return TRUE;
5683 }
5684
5685 // chat settings
5686 if (g_strcmp0(args[0], "chat") == 0) {
5687 if (g_strcmp0(args[1], "on") == 0) {
5688 cons_show("Chat notifications enabled.");
5689 prefs_set_boolean(PREF_NOTIFY_CHAT, TRUE);
5690 } else if (g_strcmp0(args[1], "off") == 0) {
5691 cons_show("Chat notifications disabled.");
5692 prefs_set_boolean(PREF_NOTIFY_CHAT, FALSE);
5693 } else if (g_strcmp0(args[1], "current") == 0) {
5694 if (g_strcmp0(args[2], "on") == 0) {
5695 cons_show("Current window chat notifications enabled.");
5696 prefs_set_boolean(PREF_NOTIFY_CHAT_CURRENT, TRUE);
5697 } else if (g_strcmp0(args[2], "off") == 0) {
5698 cons_show("Current window chat notifications disabled.");
5699 prefs_set_boolean(PREF_NOTIFY_CHAT_CURRENT, FALSE);
5700 } else {
5701 cons_show("Usage: /notify chat current on|off");
5702 }
5703 } else if (g_strcmp0(args[1], "text") == 0) {
5704 if (g_strcmp0(args[2], "on") == 0) {
5705 cons_show("Showing text in chat notifications enabled.");
5706 prefs_set_boolean(PREF_NOTIFY_CHAT_TEXT, TRUE);
5707 } else if (g_strcmp0(args[2], "off") == 0) {
5708 cons_show("Showing text in chat notifications disabled.");
5709 prefs_set_boolean(PREF_NOTIFY_CHAT_TEXT, FALSE);
5710 } else {
5711 cons_show("Usage: /notify chat text on|off");
5712 }
5713 }
5714
5715 // chat room settings
5716 } else if (g_strcmp0(args[0], "room") == 0) {
5717 if (g_strcmp0(args[1], "on") == 0) {
5718 cons_show("Room notifications enabled.");
5719 prefs_set_boolean(PREF_NOTIFY_ROOM, TRUE);
5720 } else if (g_strcmp0(args[1], "off") == 0) {
5721 cons_show("Room notifications disabled.");
5722 prefs_set_boolean(PREF_NOTIFY_ROOM, FALSE);
5723 } else if (g_strcmp0(args[1], "mention") == 0) {
5724 if (g_strcmp0(args[2], "on") == 0) {
5725 cons_show("Room notifications with mention enabled.");
5726 prefs_set_boolean(PREF_NOTIFY_ROOM_MENTION, TRUE);
5727 } else if (g_strcmp0(args[2], "off") == 0) {
5728 cons_show("Room notifications with mention disabled.");
5729 prefs_set_boolean(PREF_NOTIFY_ROOM_MENTION, FALSE);
5730 } else if (g_strcmp0(args[2], "case_sensitive") == 0) {
5731 cons_show("Room mention matching set to case sensitive.");
5732 prefs_set_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE, TRUE);
5733 } else if (g_strcmp0(args[2], "case_insensitive") == 0) {
5734 cons_show("Room mention matching set to case insensitive.");
5735 prefs_set_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE, FALSE);
5736 } else if (g_strcmp0(args[2], "word_whole") == 0) {
5737 cons_show("Room mention matching set to whole word.");
5738 prefs_set_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD, TRUE);
5739 } else if (g_strcmp0(args[2], "word_part") == 0) {
5740 cons_show("Room mention matching set to partial word.");
5741 prefs_set_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD, FALSE);
5742 } else {
5743 cons_show("Usage: /notify room mention on|off");
5744 }
5745 } else if (g_strcmp0(args[1], "current") == 0) {
5746 if (g_strcmp0(args[2], "on") == 0) {
5747 cons_show("Current window chat room message notifications enabled.");
5748 prefs_set_boolean(PREF_NOTIFY_ROOM_CURRENT, TRUE);
5749 } else if (g_strcmp0(args[2], "off") == 0) {
5750 cons_show("Current window chat room message notifications disabled.");
5751 prefs_set_boolean(PREF_NOTIFY_ROOM_CURRENT, FALSE);
5752 } else {
5753 cons_show("Usage: /notify room current on|off");
5754 }
5755 } else if (g_strcmp0(args[1], "text") == 0) {
5756 if (g_strcmp0(args[2], "on") == 0) {
5757 cons_show("Showing text in chat room message notifications enabled.");
5758 prefs_set_boolean(PREF_NOTIFY_ROOM_TEXT, TRUE);
5759 } else if (g_strcmp0(args[2], "off") == 0) {
5760 cons_show("Showing text in chat room message notifications disabled.");
5761 prefs_set_boolean(PREF_NOTIFY_ROOM_TEXT, FALSE);
5762 } else {
5763 cons_show("Usage: /notify room text on|off");
5764 }
5765 } else if (g_strcmp0(args[1], "trigger") == 0) {
5766 if (g_strcmp0(args[2], "add") == 0) {
5767 if (!args[3]) {
5768 cons_bad_cmd_usage(command);
5769 } else {
5770 gboolean res = prefs_add_room_notify_trigger(args[3]);
5771 if (res) {
5772 cons_show("Adding room notification trigger: %s", args[3]);
5773 } else {
5774 cons_show("Room notification trigger already exists: %s", args[3]);
5775 }
5776 }
5777 } else if (g_strcmp0(args[2], "remove") == 0) {
5778 if (!args[3]) {
5779 cons_bad_cmd_usage(command);
5780 } else {
5781 gboolean res = prefs_remove_room_notify_trigger(args[3]);
5782 if (res) {
5783 cons_show("Removing room notification trigger: %s", args[3]);
5784 } else {
5785 cons_show("Room notification trigger does not exist: %s", args[3]);
5786 }
5787 }
5788 } else if (g_strcmp0(args[2], "list") == 0) {
5789 GList* triggers = prefs_get_room_notify_triggers();
5790 GList* curr = triggers;
5791 if (curr) {
5792 cons_show("Room notification triggers:");
5793 } else {
5794 cons_show("No room notification triggers");
5795 }
5796 while (curr) {
5797 cons_show(" %s", curr->data);
5798 curr = g_list_next(curr);
5799 }
5800 g_list_free_full(triggers, free);
5801 } else if (g_strcmp0(args[2], "on") == 0) {
5802 cons_show("Enabling room notification triggers");
5803 prefs_set_boolean(PREF_NOTIFY_ROOM_TRIGGER, TRUE);
5804 } else if (g_strcmp0(args[2], "off") == 0) {
5805 cons_show("Disabling room notification triggers");
5806 prefs_set_boolean(PREF_NOTIFY_ROOM_TRIGGER, FALSE);
5807 } else {
5808 cons_bad_cmd_usage(command);
5809 }
5810 } else {
5811 cons_show("Usage: /notify room on|off|mention");
5812 }
5813
5814 // typing settings
5815 } else if (g_strcmp0(args[0], "typing") == 0) {
5816 if (g_strcmp0(args[1], "on") == 0) {
5817 cons_show("Typing notifications enabled.");
5818 prefs_set_boolean(PREF_NOTIFY_TYPING, TRUE);
5819 } else if (g_strcmp0(args[1], "off") == 0) {
5820 cons_show("Typing notifications disabled.");
5821 prefs_set_boolean(PREF_NOTIFY_TYPING, FALSE);
5822 } else if (g_strcmp0(args[1], "current") == 0) {
5823 if (g_strcmp0(args[2], "on") == 0) {
5824 cons_show("Current window typing notifications enabled.");
5825 prefs_set_boolean(PREF_NOTIFY_TYPING_CURRENT, TRUE);
5826 } else if (g_strcmp0(args[2], "off") == 0) {
5827 cons_show("Current window typing notifications disabled.");
5828 prefs_set_boolean(PREF_NOTIFY_TYPING_CURRENT, FALSE);
5829 } else {
5830 cons_show("Usage: /notify typing current on|off");
5831 }
5832 } else {
5833 cons_show("Usage: /notify typing on|off");
5834 }
5835
5836 // invite settings
5837 } else if (g_strcmp0(args[0], "invite") == 0) {
5838 if (g_strcmp0(args[1], "on") == 0) {
5839 cons_show("Chat room invite notifications enabled.");
5840 prefs_set_boolean(PREF_NOTIFY_INVITE, TRUE);
5841 } else if (g_strcmp0(args[1], "off") == 0) {
5842 cons_show("Chat room invite notifications disabled.");
5843 prefs_set_boolean(PREF_NOTIFY_INVITE, FALSE);
5844 } else {
5845 cons_show("Usage: /notify invite on|off");
5846 }
5847
5848 // subscription settings
5849 } else if (g_strcmp0(args[0], "sub") == 0) {
5850 if (g_strcmp0(args[1], "on") == 0) {
5851 cons_show("Subscription notifications enabled.");
5852 prefs_set_boolean(PREF_NOTIFY_SUB, TRUE);
5853 } else if (g_strcmp0(args[1], "off") == 0) {
5854 cons_show("Subscription notifications disabled.");
5855 prefs_set_boolean(PREF_NOTIFY_SUB, FALSE);
5856 } else {
5857 cons_show("Usage: /notify sub on|off");
5858 }
5859
5860 // remind settings
5861 } else if (g_strcmp0(args[0], "remind") == 0) {
5862 if (!args[1]) {
5863 cons_bad_cmd_usage(command);
5864 } else {
5865 gint period = atoi(args[1]);
5866 prefs_set_notify_remind(period);
5867 if (period == 0) {
5868 cons_show("Message reminders disabled.");
5869 } else if (period == 1) {
5870 cons_show("Message reminder period set to 1 second.");
5871 } else {
5872 cons_show("Message reminder period set to %d seconds.", period);
5873 }
5874 }
5875
5876 // current chat room settings
5877 } else if (g_strcmp0(args[0], "on") == 0) {
5878 jabber_conn_status_t conn_status = connection_get_status();
5879
5880 if (conn_status != JABBER_CONNECTED) {
5881 cons_show("You are not currently connected.");
5882 } else {
5883 ProfWin* window = wins_get_current();
5884 if (window->type != WIN_MUC) {
5885 cons_show("You must be in a chat room.");
5886 } else {
5887 ProfMucWin* mucwin = (ProfMucWin*)window;
5888 prefs_set_room_notify(mucwin->roomjid, TRUE);
5889 win_println(window, THEME_DEFAULT, "!", "Notifications enabled for %s", mucwin->roomjid);
5890 }
5891 }
5892 } else if (g_strcmp0(args[0], "off") == 0) {
5893 jabber_conn_status_t conn_status = connection_get_status();
5894
5895 if (conn_status != JABBER_CONNECTED) {
5896 cons_show("You are not currently connected.");
5897 } else {
5898 ProfWin* window = wins_get_current();
5899 if (window->type != WIN_MUC) {
5900 cons_show("You must be in a chat room.");
5901 } else {
5902 ProfMucWin* mucwin = (ProfMucWin*)window;
5903 prefs_set_room_notify(mucwin->roomjid, FALSE);
5904 win_println(window, THEME_DEFAULT, "!", "Notifications disabled for %s", mucwin->roomjid);
5905 }
5906 }
5907 } else if (g_strcmp0(args[0], "mention") == 0) {
5908 jabber_conn_status_t conn_status = connection_get_status();
5909
5910 if (conn_status != JABBER_CONNECTED) {
5911 cons_show("You are not currently connected.");
5912 } else {
5913 if (g_strcmp0(args[1], "on") == 0) {
5914 ProfWin* window = wins_get_current();
5915 if (window->type != WIN_MUC) {
5916 cons_show("You must be in a chat room.");
5917 } else {
5918 ProfMucWin* mucwin = (ProfMucWin*)window;
5919 prefs_set_room_notify_mention(mucwin->roomjid, TRUE);
5920 win_println(window, THEME_DEFAULT, "!", "Mention notifications enabled for %s", mucwin->roomjid);
5921 }
5922 } else if (g_strcmp0(args[1], "off") == 0) {
5923 ProfWin* window = wins_get_current();
5924 if (window->type != WIN_MUC) {
5925 cons_show("You must be in a chat rooms.");
5926 } else {
5927 ProfMucWin* mucwin = (ProfMucWin*)window;
5928 prefs_set_room_notify_mention(mucwin->roomjid, FALSE);
5929 win_println(window, THEME_DEFAULT, "!", "Mention notifications disabled for %s", mucwin->roomjid);
5930 }
5931 } else {
5932 cons_bad_cmd_usage(command);
5933 }
5934 }
5935 } else if (g_strcmp0(args[0], "trigger") == 0) {
5936 jabber_conn_status_t conn_status = connection_get_status();
5937
5938 if (conn_status != JABBER_CONNECTED) {
5939 cons_show("You are not currently connected.");
5940 } else {
5941 if (g_strcmp0(args[1], "on") == 0) {
5942 ProfWin* window = wins_get_current();
5943 if (window->type != WIN_MUC) {
5944 cons_show("You must be in a chat room.");
5945 } else {
5946 ProfMucWin* mucwin = (ProfMucWin*)window;
5947 prefs_set_room_notify_trigger(mucwin->roomjid, TRUE);
5948 win_println(window, THEME_DEFAULT, "!", "Custom trigger notifications enabled for %s", mucwin->roomjid);
5949 }
5950 } else if (g_strcmp0(args[1], "off") == 0) {
5951 ProfWin* window = wins_get_current();
5952 if (window->type != WIN_MUC) {
5953 cons_show("You must be in a chat rooms.");
5954 } else {
5955 ProfMucWin* mucwin = (ProfMucWin*)window;
5956 prefs_set_room_notify_trigger(mucwin->roomjid, FALSE);
5957 win_println(window, THEME_DEFAULT, "!", "Custom trigger notifications disabled for %s", mucwin->roomjid);
5958 }
5959 } else {
5960 cons_bad_cmd_usage(command);
5961 }
5962 }
5963 } else if (g_strcmp0(args[0], "reset") == 0) {
5964 jabber_conn_status_t conn_status = connection_get_status();
5965
5966 if (conn_status != JABBER_CONNECTED) {
5967 cons_show("You are not currently connected.");
5968 } else {
5969 ProfWin* window = wins_get_current();
5970 if (window->type != WIN_MUC) {
5971 cons_show("You must be in a chat room.");
5972 } else {
5973 ProfMucWin* mucwin = (ProfMucWin*)window;
5974 gboolean res = prefs_reset_room_notify(mucwin->roomjid);
5975 if (res) {
5976 win_println(window, THEME_DEFAULT, "!", "Notification settings set to global defaults for %s", mucwin->roomjid);
5977 } else {
5978 win_println(window, THEME_DEFAULT, "!", "No custom notification settings for %s", mucwin->roomjid);
5979 }
5980 }
5981 }
5982 } else {
5983 cons_bad_cmd_usage(command);
5984 }
5985
5986 return TRUE;
5987 }
5988
5989 gboolean
cmd_inpblock(ProfWin * window,const char * const command,gchar ** args)5990 cmd_inpblock(ProfWin* window, const char* const command, gchar** args)
5991 {
5992 char* subcmd = args[0];
5993 char* value = args[1];
5994
5995 if (g_strcmp0(subcmd, "timeout") == 0) {
5996 if (value == NULL) {
5997 cons_bad_cmd_usage(command);
5998 return TRUE;
5999 }
6000
6001 int intval = 0;
6002 char* err_msg = NULL;
6003 gboolean res = strtoi_range(value, &intval, 1, 1000, &err_msg);
6004 if (res) {
6005 cons_show("Input blocking set to %d milliseconds.", intval);
6006 prefs_set_inpblock(intval);
6007 inp_nonblocking(FALSE);
6008 } else {
6009 cons_show(err_msg);
6010 free(err_msg);
6011 }
6012
6013 return TRUE;
6014 }
6015
6016 if (g_strcmp0(subcmd, "dynamic") == 0) {
6017 if (value == NULL) {
6018 cons_bad_cmd_usage(command);
6019 return TRUE;
6020 }
6021
6022 if (g_strcmp0(value, "on") != 0 && g_strcmp0(value, "off") != 0) {
6023 cons_show("Dynamic must be one of 'on' or 'off'");
6024 return TRUE;
6025 }
6026
6027 _cmd_set_boolean_preference(value, command, "Dynamic input blocking", PREF_INPBLOCK_DYNAMIC);
6028 return TRUE;
6029 }
6030
6031 cons_bad_cmd_usage(command);
6032
6033 return TRUE;
6034 }
6035
6036 gboolean
cmd_titlebar(ProfWin * window,const char * const command,gchar ** args)6037 cmd_titlebar(ProfWin* window, const char* const command, gchar** args)
6038 {
6039 if (g_strcmp0(args[0], "up") == 0) {
6040 gboolean result = prefs_titlebar_pos_up();
6041 if (result) {
6042 ui_resize();
6043 cons_show("Title bar moved up.");
6044 } else {
6045 cons_show("Could not move title bar up.");
6046 }
6047
6048 return TRUE;
6049 }
6050 if (g_strcmp0(args[0], "down") == 0) {
6051 gboolean result = prefs_titlebar_pos_down();
6052 if (result) {
6053 ui_resize();
6054 cons_show("Title bar moved down.");
6055 } else {
6056 cons_show("Could not move title bar down.");
6057 }
6058
6059 return TRUE;
6060 }
6061
6062 cons_bad_cmd_usage(command);
6063
6064 return TRUE;
6065 }
6066
6067 gboolean
cmd_titlebar_show_hide(ProfWin * window,const char * const command,gchar ** args)6068 cmd_titlebar_show_hide(ProfWin* window, const char* const command, gchar** args)
6069 {
6070 if (args[1] != NULL) {
6071 if (g_strcmp0(args[0], "show") == 0) {
6072
6073 if (g_strcmp0(args[1], "tls") == 0) {
6074 cons_show("TLS titlebar indicator enabled.");
6075 prefs_set_boolean(PREF_TLS_SHOW, TRUE);
6076 } else if (g_strcmp0(args[1], "encwarn") == 0) {
6077 cons_show("Encryption warning titlebar indicator enabled.");
6078 prefs_set_boolean(PREF_ENC_WARN, TRUE);
6079 } else if (g_strcmp0(args[1], "resource") == 0) {
6080 cons_show("Showing resource in titlebar enabled.");
6081 prefs_set_boolean(PREF_RESOURCE_TITLE, TRUE);
6082 } else if (g_strcmp0(args[1], "presence") == 0) {
6083 cons_show("Showing contact presence in titlebar enabled.");
6084 prefs_set_boolean(PREF_PRESENCE, TRUE);
6085 } else if (g_strcmp0(args[1], "jid") == 0) {
6086 cons_show("Showing MUC JID in titlebar as title enabled.");
6087 prefs_set_boolean(PREF_TITLEBAR_MUC_TITLE_JID, TRUE);
6088 } else if (g_strcmp0(args[1], "name") == 0) {
6089 cons_show("Showing MUC name in titlebar as title enabled.");
6090 prefs_set_boolean(PREF_TITLEBAR_MUC_TITLE_NAME, TRUE);
6091 } else {
6092 cons_bad_cmd_usage(command);
6093 }
6094 } else if (g_strcmp0(args[0], "hide") == 0) {
6095
6096 if (g_strcmp0(args[1], "tls") == 0) {
6097 cons_show("TLS titlebar indicator disabled.");
6098 prefs_set_boolean(PREF_TLS_SHOW, FALSE);
6099 } else if (g_strcmp0(args[1], "encwarn") == 0) {
6100 cons_show("Encryption warning titlebar indicator disabled.");
6101 prefs_set_boolean(PREF_ENC_WARN, FALSE);
6102 } else if (g_strcmp0(args[1], "resource") == 0) {
6103 cons_show("Showing resource in titlebar disabled.");
6104 prefs_set_boolean(PREF_RESOURCE_TITLE, FALSE);
6105 } else if (g_strcmp0(args[1], "presence") == 0) {
6106 cons_show("Showing contact presence in titlebar disabled.");
6107 prefs_set_boolean(PREF_PRESENCE, FALSE);
6108 } else if (g_strcmp0(args[1], "jid") == 0) {
6109 cons_show("Showing MUC JID in titlebar as title disabled.");
6110 prefs_set_boolean(PREF_TITLEBAR_MUC_TITLE_JID, FALSE);
6111 } else if (g_strcmp0(args[1], "name") == 0) {
6112 cons_show("Showing MUC name in titlebar as title disabled.");
6113 prefs_set_boolean(PREF_TITLEBAR_MUC_TITLE_NAME, FALSE);
6114 } else {
6115 cons_bad_cmd_usage(command);
6116 }
6117 } else {
6118 cons_bad_cmd_usage(command);
6119 }
6120 }
6121
6122 return TRUE;
6123 }
6124
6125 gboolean
cmd_mainwin(ProfWin * window,const char * const command,gchar ** args)6126 cmd_mainwin(ProfWin* window, const char* const command, gchar** args)
6127 {
6128 if (g_strcmp0(args[0], "up") == 0) {
6129 gboolean result = prefs_mainwin_pos_up();
6130 if (result) {
6131 ui_resize();
6132 cons_show("Main window moved up.");
6133 } else {
6134 cons_show("Could not move main window up.");
6135 }
6136
6137 return TRUE;
6138 }
6139 if (g_strcmp0(args[0], "down") == 0) {
6140 gboolean result = prefs_mainwin_pos_down();
6141 if (result) {
6142 ui_resize();
6143 cons_show("Main window moved down.");
6144 } else {
6145 cons_show("Could not move main window down.");
6146 }
6147
6148 return TRUE;
6149 }
6150
6151 cons_bad_cmd_usage(command);
6152
6153 return TRUE;
6154 }
6155
6156 gboolean
cmd_statusbar(ProfWin * window,const char * const command,gchar ** args)6157 cmd_statusbar(ProfWin* window, const char* const command, gchar** args)
6158 {
6159 if (g_strcmp0(args[0], "show") == 0) {
6160 if (g_strcmp0(args[1], "name") == 0) {
6161 prefs_set_boolean(PREF_STATUSBAR_SHOW_NAME, TRUE);
6162 cons_show("Enabled showing tab names.");
6163 ui_resize();
6164 return TRUE;
6165 }
6166 if (g_strcmp0(args[1], "number") == 0) {
6167 prefs_set_boolean(PREF_STATUSBAR_SHOW_NUMBER, TRUE);
6168 cons_show("Enabled showing tab numbers.");
6169 ui_resize();
6170 return TRUE;
6171 }
6172 if (g_strcmp0(args[1], "read") == 0) {
6173 prefs_set_boolean(PREF_STATUSBAR_SHOW_READ, TRUE);
6174 cons_show("Enabled showing inactive tabs.");
6175 ui_resize();
6176 return TRUE;
6177 }
6178 cons_bad_cmd_usage(command);
6179 return TRUE;
6180 }
6181
6182 if (g_strcmp0(args[0], "hide") == 0) {
6183 if (g_strcmp0(args[1], "name") == 0) {
6184 if (prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER) == FALSE) {
6185 cons_show("Cannot disable both names and numbers in statusbar.");
6186 cons_show("Use '/statusbar maxtabs 0' to hide tabs.");
6187 return TRUE;
6188 }
6189 prefs_set_boolean(PREF_STATUSBAR_SHOW_NAME, FALSE);
6190 cons_show("Disabled showing tab names.");
6191 ui_resize();
6192 return TRUE;
6193 }
6194 if (g_strcmp0(args[1], "number") == 0) {
6195 if (prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME) == FALSE) {
6196 cons_show("Cannot disable both names and numbers in statusbar.");
6197 cons_show("Use '/statusbar maxtabs 0' to hide tabs.");
6198 return TRUE;
6199 }
6200 prefs_set_boolean(PREF_STATUSBAR_SHOW_NUMBER, FALSE);
6201 cons_show("Disabled showing tab numbers.");
6202 ui_resize();
6203 return TRUE;
6204 }
6205 if (g_strcmp0(args[1], "read") == 0) {
6206 prefs_set_boolean(PREF_STATUSBAR_SHOW_READ, FALSE);
6207 cons_show("Disabled showing inactive tabs.");
6208 ui_resize();
6209 return TRUE;
6210 }
6211 cons_bad_cmd_usage(command);
6212 return TRUE;
6213 }
6214
6215 if (g_strcmp0(args[0], "maxtabs") == 0) {
6216 if (args[1] == NULL) {
6217 cons_bad_cmd_usage(command);
6218 return TRUE;
6219 }
6220
6221 char* value = args[1];
6222 int intval = 0;
6223 char* err_msg = NULL;
6224 gboolean res = strtoi_range(value, &intval, 0, INT_MAX, &err_msg);
6225 if (res) {
6226 if (intval < 0 || intval > 10) {
6227 cons_bad_cmd_usage(command);
6228 return TRUE;
6229 }
6230
6231 prefs_set_statusbartabs(intval);
6232 if (intval == 0) {
6233 cons_show("Status bar tabs disabled.");
6234 } else {
6235 cons_show("Status bar tabs set to %d.", intval);
6236 }
6237 ui_resize();
6238 return TRUE;
6239 } else {
6240 cons_show(err_msg);
6241 cons_bad_cmd_usage(command);
6242 free(err_msg);
6243 return TRUE;
6244 }
6245 }
6246
6247 if (g_strcmp0(args[0], "tablen") == 0) {
6248 if (args[1] == NULL) {
6249 cons_bad_cmd_usage(command);
6250 return TRUE;
6251 }
6252
6253 char* value = args[1];
6254 int intval = 0;
6255 char* err_msg = NULL;
6256 gboolean res = strtoi_range(value, &intval, 0, INT_MAX, &err_msg);
6257 if (res) {
6258 if (intval < 0) {
6259 cons_bad_cmd_usage(command);
6260 return TRUE;
6261 }
6262
6263 prefs_set_statusbartablen(intval);
6264 if (intval == 0) {
6265 cons_show("Maximum tab length disabled.");
6266 } else {
6267 cons_show("Maximum tab length set to %d.", intval);
6268 }
6269 ui_resize();
6270 return TRUE;
6271 } else {
6272 cons_show(err_msg);
6273 cons_bad_cmd_usage(command);
6274 free(err_msg);
6275 return TRUE;
6276 }
6277 }
6278
6279 if (g_strcmp0(args[0], "self") == 0) {
6280 if (g_strcmp0(args[1], "barejid") == 0) {
6281 prefs_set_string(PREF_STATUSBAR_SELF, "barejid");
6282 cons_show("Using barejid for statusbar title.");
6283 ui_resize();
6284 return TRUE;
6285 }
6286 if (g_strcmp0(args[1], "fulljid") == 0) {
6287 prefs_set_string(PREF_STATUSBAR_SELF, "fulljid");
6288 cons_show("Using fulljid for statusbar title.");
6289 ui_resize();
6290 return TRUE;
6291 }
6292 if (g_strcmp0(args[1], "user") == 0) {
6293 prefs_set_string(PREF_STATUSBAR_SELF, "user");
6294 cons_show("Using user for statusbar title.");
6295 ui_resize();
6296 return TRUE;
6297 }
6298 if (g_strcmp0(args[1], "off") == 0) {
6299 prefs_set_string(PREF_STATUSBAR_SELF, "off");
6300 cons_show("Disabling statusbar title.");
6301 ui_resize();
6302 return TRUE;
6303 }
6304 cons_bad_cmd_usage(command);
6305 return TRUE;
6306 }
6307
6308 if (g_strcmp0(args[0], "chat") == 0) {
6309 if (g_strcmp0(args[1], "jid") == 0) {
6310 prefs_set_string(PREF_STATUSBAR_CHAT, "jid");
6311 cons_show("Using jid for chat tabs.");
6312 ui_resize();
6313 return TRUE;
6314 }
6315 if (g_strcmp0(args[1], "user") == 0) {
6316 prefs_set_string(PREF_STATUSBAR_CHAT, "user");
6317 cons_show("Using user for chat tabs.");
6318 ui_resize();
6319 return TRUE;
6320 }
6321 cons_bad_cmd_usage(command);
6322 return TRUE;
6323 }
6324
6325 if (g_strcmp0(args[0], "room") == 0) {
6326 if (g_strcmp0(args[1], "jid") == 0) {
6327 prefs_set_string(PREF_STATUSBAR_ROOM, "jid");
6328 cons_show("Using jid for room tabs.");
6329 ui_resize();
6330 return TRUE;
6331 }
6332 if (g_strcmp0(args[1], "room") == 0) {
6333 prefs_set_string(PREF_STATUSBAR_ROOM, "room");
6334 cons_show("Using room name for room tabs.");
6335 ui_resize();
6336 return TRUE;
6337 }
6338 cons_bad_cmd_usage(command);
6339 return TRUE;
6340 }
6341
6342 if (g_strcmp0(args[0], "up") == 0) {
6343 gboolean result = prefs_statusbar_pos_up();
6344 if (result) {
6345 ui_resize();
6346 cons_show("Status bar moved up");
6347 } else {
6348 cons_show("Could not move status bar up.");
6349 }
6350
6351 return TRUE;
6352 }
6353 if (g_strcmp0(args[0], "down") == 0) {
6354 gboolean result = prefs_statusbar_pos_down();
6355 if (result) {
6356 ui_resize();
6357 cons_show("Status bar moved down.");
6358 } else {
6359 cons_show("Could not move status bar down.");
6360 }
6361
6362 return TRUE;
6363 }
6364
6365 cons_bad_cmd_usage(command);
6366
6367 return TRUE;
6368 }
6369
6370 gboolean
cmd_inputwin(ProfWin * window,const char * const command,gchar ** args)6371 cmd_inputwin(ProfWin* window, const char* const command, gchar** args)
6372 {
6373 if (g_strcmp0(args[0], "up") == 0) {
6374 gboolean result = prefs_inputwin_pos_up();
6375 if (result) {
6376 ui_resize();
6377 cons_show("Input window moved up.");
6378 } else {
6379 cons_show("Could not move input window up.");
6380 }
6381
6382 return TRUE;
6383 }
6384 if (g_strcmp0(args[0], "down") == 0) {
6385 gboolean result = prefs_inputwin_pos_down();
6386 if (result) {
6387 ui_resize();
6388 cons_show("Input window moved down.");
6389 } else {
6390 cons_show("Could not move input window down.");
6391 }
6392
6393 return TRUE;
6394 }
6395
6396 cons_bad_cmd_usage(command);
6397
6398 return TRUE;
6399 }
6400
6401 gboolean
cmd_log(ProfWin * window,const char * const command,gchar ** args)6402 cmd_log(ProfWin* window, const char* const command, gchar** args)
6403 {
6404 char* subcmd = args[0];
6405 char* value = args[1];
6406
6407 if (strcmp(subcmd, "maxsize") == 0) {
6408 if (value == NULL) {
6409 cons_bad_cmd_usage(command);
6410 return TRUE;
6411 }
6412
6413 int intval = 0;
6414 char* err_msg = NULL;
6415 gboolean res = strtoi_range(value, &intval, PREFS_MIN_LOG_SIZE, INT_MAX, &err_msg);
6416 if (res) {
6417 prefs_set_max_log_size(intval);
6418 cons_show("Log maximum size set to %d bytes", intval);
6419 } else {
6420 cons_show(err_msg);
6421 free(err_msg);
6422 }
6423 return TRUE;
6424 }
6425
6426 if (strcmp(subcmd, "rotate") == 0) {
6427 if (value == NULL) {
6428 cons_bad_cmd_usage(command);
6429 return TRUE;
6430 }
6431 _cmd_set_boolean_preference(value, command, "Log rotate", PREF_LOG_ROTATE);
6432 return TRUE;
6433 }
6434
6435 if (strcmp(subcmd, "shared") == 0) {
6436 if (value == NULL) {
6437 cons_bad_cmd_usage(command);
6438 return TRUE;
6439 }
6440 _cmd_set_boolean_preference(value, command, "Shared log", PREF_LOG_SHARED);
6441 cons_show("Setting only takes effect after saving and restarting Profanity.");
6442 return TRUE;
6443 }
6444
6445 if (strcmp(subcmd, "where") == 0) {
6446 cons_show("Log file: %s", get_log_file_location());
6447 return TRUE;
6448 }
6449
6450 cons_bad_cmd_usage(command);
6451
6452 /* TODO: make 'level' subcommand for debug level */
6453
6454 return TRUE;
6455 }
6456
6457 gboolean
cmd_reconnect(ProfWin * window,const char * const command,gchar ** args)6458 cmd_reconnect(ProfWin* window, const char* const command, gchar** args)
6459 {
6460 char* value = args[0];
6461
6462 int intval = 0;
6463 char* err_msg = NULL;
6464 gboolean res = strtoi_range(value, &intval, 0, INT_MAX, &err_msg);
6465 if (res) {
6466 prefs_set_reconnect(intval);
6467 if (intval == 0) {
6468 cons_show("Reconnect disabled.", intval);
6469 } else {
6470 cons_show("Reconnect interval set to %d seconds.", intval);
6471 }
6472 } else {
6473 cons_show(err_msg);
6474 cons_bad_cmd_usage(command);
6475 free(err_msg);
6476 }
6477
6478 return TRUE;
6479 }
6480
6481 gboolean
cmd_autoping(ProfWin * window,const char * const command,gchar ** args)6482 cmd_autoping(ProfWin* window, const char* const command, gchar** args)
6483 {
6484 char* cmd = args[0];
6485 char* value = args[1];
6486
6487 if (g_strcmp0(cmd, "set") == 0) {
6488 int intval = 0;
6489 char* err_msg = NULL;
6490 gboolean res = strtoi_range(value, &intval, 0, INT_MAX, &err_msg);
6491 if (res) {
6492 prefs_set_autoping(intval);
6493 iq_set_autoping(intval);
6494 if (intval == 0) {
6495 cons_show("Autoping disabled.");
6496 } else {
6497 cons_show("Autoping interval set to %d seconds.", intval);
6498 }
6499 } else {
6500 cons_show(err_msg);
6501 cons_bad_cmd_usage(command);
6502 free(err_msg);
6503 }
6504
6505 } else if (g_strcmp0(cmd, "timeout") == 0) {
6506 int intval = 0;
6507 char* err_msg = NULL;
6508 gboolean res = strtoi_range(value, &intval, 0, INT_MAX, &err_msg);
6509 if (res) {
6510 prefs_set_autoping_timeout(intval);
6511 if (intval == 0) {
6512 cons_show("Autoping timeout disabled.");
6513 } else {
6514 cons_show("Autoping timeout set to %d seconds.", intval);
6515 }
6516 } else {
6517 cons_show(err_msg);
6518 cons_bad_cmd_usage(command);
6519 free(err_msg);
6520 }
6521
6522 } else {
6523 cons_bad_cmd_usage(command);
6524 }
6525
6526 return TRUE;
6527 }
6528
6529 gboolean
cmd_ping(ProfWin * window,const char * const command,gchar ** args)6530 cmd_ping(ProfWin* window, const char* const command, gchar** args)
6531 {
6532 jabber_conn_status_t conn_status = connection_get_status();
6533
6534 if (conn_status != JABBER_CONNECTED) {
6535 cons_show("You are not currently connected.");
6536 return TRUE;
6537 }
6538
6539 if (args[0] == NULL && connection_supports(XMPP_FEATURE_PING) == FALSE) {
6540 cons_show("Server does not support ping requests (%s).", XMPP_FEATURE_PING);
6541 return TRUE;
6542 }
6543
6544 if (args[0] != NULL && caps_jid_has_feature(args[0], XMPP_FEATURE_PING) == FALSE) {
6545 cons_show("%s does not support ping requests.", args[0]);
6546 return TRUE;
6547 }
6548
6549 iq_send_ping(args[0]);
6550
6551 if (args[0] == NULL) {
6552 cons_show("Pinged server...");
6553 } else {
6554 cons_show("Pinged %s...", args[0]);
6555 }
6556 return TRUE;
6557 }
6558
6559 gboolean
cmd_autoaway(ProfWin * window,const char * const command,gchar ** args)6560 cmd_autoaway(ProfWin* window, const char* const command, gchar** args)
6561 {
6562 if ((g_strcmp0(args[0], "mode") != 0) && (g_strcmp0(args[0], "time") != 0) && (g_strcmp0(args[0], "message") != 0) && (g_strcmp0(args[0], "check") != 0)) {
6563 cons_show("Setting must be one of 'mode', 'time', 'message' or 'check'");
6564 return TRUE;
6565 }
6566
6567 if (g_strcmp0(args[0], "mode") == 0) {
6568 if ((g_strcmp0(args[1], "idle") != 0) && (g_strcmp0(args[1], "away") != 0) && (g_strcmp0(args[1], "off") != 0)) {
6569 cons_show("Mode must be one of 'idle', 'away' or 'off'");
6570 } else {
6571 prefs_set_string(PREF_AUTOAWAY_MODE, args[1]);
6572 cons_show("Auto away mode set to: %s.", args[1]);
6573 }
6574
6575 return TRUE;
6576 }
6577
6578 if ((g_strcmp0(args[0], "time") == 0) && (args[2] != NULL)) {
6579 if (g_strcmp0(args[1], "away") == 0) {
6580 int minutesval = 0;
6581 char* err_msg = NULL;
6582 gboolean res = strtoi_range(args[2], &minutesval, 1, INT_MAX, &err_msg);
6583 if (res) {
6584 prefs_set_autoaway_time(minutesval);
6585 if (minutesval == 1) {
6586 cons_show("Auto away time set to: 1 minute.");
6587 } else {
6588 cons_show("Auto away time set to: %d minutes.", minutesval);
6589 }
6590 } else {
6591 cons_show(err_msg);
6592 free(err_msg);
6593 }
6594
6595 return TRUE;
6596 } else if (g_strcmp0(args[1], "xa") == 0) {
6597 int minutesval = 0;
6598 char* err_msg = NULL;
6599 gboolean res = strtoi_range(args[2], &minutesval, 0, INT_MAX, &err_msg);
6600 if (res) {
6601 int away_time = prefs_get_autoaway_time();
6602 if (minutesval != 0 && minutesval <= away_time) {
6603 cons_show("Auto xa time must be larger than auto away time.");
6604 } else {
6605 prefs_set_autoxa_time(minutesval);
6606 if (minutesval == 0) {
6607 cons_show("Auto xa time disabled.");
6608 } else if (minutesval == 1) {
6609 cons_show("Auto xa time set to: 1 minute.");
6610 } else {
6611 cons_show("Auto xa time set to: %d minutes.", minutesval);
6612 }
6613 }
6614 } else {
6615 cons_show(err_msg);
6616 free(err_msg);
6617 }
6618
6619 return TRUE;
6620 } else {
6621 cons_bad_cmd_usage(command);
6622 return TRUE;
6623 }
6624 } else {
6625 cons_bad_cmd_usage(command);
6626 return TRUE;
6627 }
6628
6629 if (g_strcmp0(args[0], "message") == 0) {
6630 if (g_strcmp0(args[1], "away") == 0 && args[2] != NULL) {
6631 if (g_strcmp0(args[2], "off") == 0) {
6632 prefs_set_string(PREF_AUTOAWAY_MESSAGE, NULL);
6633 cons_show("Auto away message cleared.");
6634 } else {
6635 prefs_set_string(PREF_AUTOAWAY_MESSAGE, args[2]);
6636 cons_show("Auto away message set to: \"%s\".", args[2]);
6637 }
6638
6639 return TRUE;
6640 } else if (g_strcmp0(args[1], "xa") == 0) {
6641 if (g_strcmp0(args[2], "off") == 0) {
6642 prefs_set_string(PREF_AUTOXA_MESSAGE, NULL);
6643 cons_show("Auto xa message cleared.");
6644 } else {
6645 prefs_set_string(PREF_AUTOXA_MESSAGE, args[2]);
6646 cons_show("Auto xa message set to: \"%s\".", args[2]);
6647 }
6648
6649 return TRUE;
6650 } else {
6651 cons_bad_cmd_usage(command);
6652 return TRUE;
6653 }
6654 }
6655
6656 if (g_strcmp0(args[0], "check") == 0) {
6657 _cmd_set_boolean_preference(args[1], command, "Online check", PREF_AUTOAWAY_CHECK);
6658 return TRUE;
6659 }
6660
6661 return TRUE;
6662 }
6663
6664 gboolean
cmd_priority(ProfWin * window,const char * const command,gchar ** args)6665 cmd_priority(ProfWin* window, const char* const command, gchar** args)
6666 {
6667 jabber_conn_status_t conn_status = connection_get_status();
6668
6669 if (conn_status != JABBER_CONNECTED) {
6670 cons_show("You are not currently connected.");
6671 return TRUE;
6672 }
6673
6674 char* value = args[0];
6675
6676 int intval = 0;
6677 char* err_msg = NULL;
6678 gboolean res = strtoi_range(value, &intval, -128, 127, &err_msg);
6679 if (res) {
6680 accounts_set_priority_all(session_get_account_name(), intval);
6681 resource_presence_t last_presence = accounts_get_last_presence(session_get_account_name());
6682 cl_ev_presence_send(last_presence, 0);
6683 cons_show("Priority set to %d.", intval);
6684 } else {
6685 cons_show(err_msg);
6686 free(err_msg);
6687 }
6688
6689 return TRUE;
6690 }
6691
6692 gboolean
cmd_vercheck(ProfWin * window,const char * const command,gchar ** args)6693 cmd_vercheck(ProfWin* window, const char* const command, gchar** args)
6694 {
6695 int num_args = g_strv_length(args);
6696
6697 if (num_args == 0) {
6698 cons_check_version(TRUE);
6699 return TRUE;
6700 } else {
6701 _cmd_set_boolean_preference(args[0], command, "Version checking", PREF_VERCHECK);
6702 return TRUE;
6703 }
6704 }
6705
6706 gboolean
cmd_xmlconsole(ProfWin * window,const char * const command,gchar ** args)6707 cmd_xmlconsole(ProfWin* window, const char* const command, gchar** args)
6708 {
6709 ProfXMLWin* xmlwin = wins_get_xmlconsole();
6710 if (xmlwin) {
6711 ui_focus_win((ProfWin*)xmlwin);
6712 } else {
6713 ProfWin* window = wins_new_xmlconsole();
6714 ui_focus_win(window);
6715 }
6716
6717 return TRUE;
6718 }
6719
6720 gboolean
cmd_flash(ProfWin * window,const char * const command,gchar ** args)6721 cmd_flash(ProfWin* window, const char* const command, gchar** args)
6722 {
6723 _cmd_set_boolean_preference(args[0], command, "Screen flash", PREF_FLASH);
6724 return TRUE;
6725 }
6726
6727 gboolean
cmd_tray(ProfWin * window,const char * const command,gchar ** args)6728 cmd_tray(ProfWin* window, const char* const command, gchar** args)
6729 {
6730 #ifdef HAVE_GTK
6731 if (g_strcmp0(args[0], "timer") == 0) {
6732 if (args[1] == NULL) {
6733 cons_bad_cmd_usage(command);
6734 return TRUE;
6735 }
6736
6737 if (prefs_get_boolean(PREF_TRAY) == FALSE) {
6738 cons_show("Tray icon not currently enabled, see /help tray");
6739 return TRUE;
6740 }
6741
6742 int intval = 0;
6743 char* err_msg = NULL;
6744 gboolean res = strtoi_range(args[1], &intval, 1, 10, &err_msg);
6745 if (res) {
6746 if (intval == 1) {
6747 cons_show("Tray timer set to 1 second.");
6748 } else {
6749 cons_show("Tray timer set to %d seconds.", intval);
6750 }
6751 prefs_set_tray_timer(intval);
6752 if (prefs_get_boolean(PREF_TRAY)) {
6753 tray_set_timer(intval);
6754 }
6755 } else {
6756 cons_show(err_msg);
6757 free(err_msg);
6758 }
6759
6760 return TRUE;
6761 } else if (g_strcmp0(args[0], "read") == 0) {
6762 if (prefs_get_boolean(PREF_TRAY) == FALSE) {
6763 cons_show("Tray icon not currently enabled, see /help tray");
6764 } else if (g_strcmp0(args[1], "on") == 0) {
6765 prefs_set_boolean(PREF_TRAY_READ, TRUE);
6766 cons_show("Tray icon enabled when no unread messages.");
6767 } else if (g_strcmp0(args[1], "off") == 0) {
6768 prefs_set_boolean(PREF_TRAY_READ, FALSE);
6769 cons_show("Tray icon disabled when no unread messages.");
6770 } else {
6771 cons_bad_cmd_usage(command);
6772 }
6773
6774 return TRUE;
6775 } else {
6776 gboolean old = prefs_get_boolean(PREF_TRAY);
6777 _cmd_set_boolean_preference(args[0], command, "Tray icon", PREF_TRAY);
6778 gboolean new = prefs_get_boolean(PREF_TRAY);
6779 if (old != new) {
6780 if (new) {
6781 tray_enable();
6782 } else {
6783 tray_disable();
6784 }
6785 }
6786
6787 return TRUE;
6788 }
6789 #else
6790 cons_show("This version of Profanity has not been built with GTK Tray Icon support enabled");
6791 return TRUE;
6792 #endif
6793 }
6794
6795 gboolean
cmd_intype(ProfWin * window,const char * const command,gchar ** args)6796 cmd_intype(ProfWin* window, const char* const command, gchar** args)
6797 {
6798 if (g_strcmp0(args[0], "console") == 0) {
6799 _cmd_set_boolean_preference(args[1], command, "Show contact typing in console", PREF_INTYPE_CONSOLE);
6800 } else if (g_strcmp0(args[0], "titlebar") == 0) {
6801 _cmd_set_boolean_preference(args[1], command, "Show contact typing in titlebar", PREF_INTYPE);
6802 } else {
6803 cons_bad_cmd_usage(command);
6804 }
6805
6806 return TRUE;
6807 }
6808
6809 gboolean
cmd_splash(ProfWin * window,const char * const command,gchar ** args)6810 cmd_splash(ProfWin* window, const char* const command, gchar** args)
6811 {
6812 _cmd_set_boolean_preference(args[0], command, "Splash screen", PREF_SPLASH);
6813 return TRUE;
6814 }
6815
6816 gboolean
cmd_autoconnect(ProfWin * window,const char * const command,gchar ** args)6817 cmd_autoconnect(ProfWin* window, const char* const command, gchar** args)
6818 {
6819 if (strcmp(args[0], "off") == 0) {
6820 prefs_set_string(PREF_CONNECT_ACCOUNT, NULL);
6821 cons_show("Autoconnect account disabled.");
6822 } else if (strcmp(args[0], "set") == 0) {
6823 if (args[1] == NULL || strlen(args[1]) == 0) {
6824 cons_bad_cmd_usage(command);
6825 } else {
6826 if (accounts_account_exists(args[1])) {
6827 prefs_set_string(PREF_CONNECT_ACCOUNT, args[1]);
6828 cons_show("Autoconnect account set to: %s.", args[1]);
6829 } else {
6830 cons_show_error("Account '%s' does not exist.", args[1]);
6831 }
6832 }
6833 } else {
6834 cons_bad_cmd_usage(command);
6835 }
6836 return TRUE;
6837 }
6838
6839 gboolean
cmd_logging(ProfWin * window,const char * const command,gchar ** args)6840 cmd_logging(ProfWin* window, const char* const command, gchar** args)
6841 {
6842 if (args[0] == NULL) {
6843 cons_logging_setting();
6844 return TRUE;
6845 }
6846
6847 if (strcmp(args[0], "chat") == 0 && args[1] != NULL) {
6848 _cmd_set_boolean_preference(args[1], command, "Chat logging", PREF_CHLOG);
6849
6850 // if set to off, disable history
6851 if (strcmp(args[1], "off") == 0) {
6852 prefs_set_boolean(PREF_HISTORY, FALSE);
6853 }
6854
6855 return TRUE;
6856 } else if (g_strcmp0(args[0], "group") == 0 && args[1] != NULL) {
6857 if (g_strcmp0(args[1], "on") == 0 || g_strcmp0(args[1], "off") == 0) {
6858 _cmd_set_boolean_preference(args[1], command, "Groupchat logging", PREF_GRLOG);
6859 return TRUE;
6860 }
6861 }
6862
6863 cons_bad_cmd_usage(command);
6864 return TRUE;
6865 }
6866
6867 gboolean
cmd_history(ProfWin * window,const char * const command,gchar ** args)6868 cmd_history(ProfWin* window, const char* const command, gchar** args)
6869 {
6870 if (args[0] == NULL) {
6871 return FALSE;
6872 }
6873
6874 _cmd_set_boolean_preference(args[0], command, "Chat history", PREF_HISTORY);
6875
6876 // if set to on, set chlog (/logging chat on)
6877 if (strcmp(args[0], "on") == 0) {
6878 prefs_set_boolean(PREF_CHLOG, TRUE);
6879 }
6880
6881 return TRUE;
6882 }
6883
6884 gboolean
cmd_carbons(ProfWin * window,const char * const command,gchar ** args)6885 cmd_carbons(ProfWin* window, const char* const command, gchar** args)
6886 {
6887 if (args[0] == NULL) {
6888 return FALSE;
6889 }
6890
6891 _cmd_set_boolean_preference(args[0], command, "Message carbons preference", PREF_CARBONS);
6892
6893 jabber_conn_status_t conn_status = connection_get_status();
6894
6895 if (conn_status == JABBER_CONNECTED) {
6896 // enable carbons
6897 if (strcmp(args[0], "on") == 0) {
6898 iq_enable_carbons();
6899 } else if (strcmp(args[0], "off") == 0) {
6900 iq_disable_carbons();
6901 }
6902 }
6903
6904 return TRUE;
6905 }
6906
6907 gboolean
cmd_receipts(ProfWin * window,const char * const command,gchar ** args)6908 cmd_receipts(ProfWin* window, const char* const command, gchar** args)
6909 {
6910 if (g_strcmp0(args[0], "send") == 0) {
6911 _cmd_set_boolean_preference(args[1], command, "Send delivery receipts", PREF_RECEIPTS_SEND);
6912 if (g_strcmp0(args[1], "on") == 0) {
6913 caps_add_feature(XMPP_FEATURE_RECEIPTS);
6914 }
6915 if (g_strcmp0(args[1], "off") == 0) {
6916 caps_remove_feature(XMPP_FEATURE_RECEIPTS);
6917 }
6918 } else if (g_strcmp0(args[0], "request") == 0) {
6919 _cmd_set_boolean_preference(args[1], command, "Request delivery receipts", PREF_RECEIPTS_REQUEST);
6920 } else {
6921 cons_bad_cmd_usage(command);
6922 }
6923
6924 return TRUE;
6925 }
6926
6927 gboolean
cmd_plugins_sourcepath(ProfWin * window,const char * const command,gchar ** args)6928 cmd_plugins_sourcepath(ProfWin* window, const char* const command, gchar** args)
6929 {
6930 if (args[1] == NULL) {
6931 char* sourcepath = prefs_get_string(PREF_PLUGINS_SOURCEPATH);
6932 if (sourcepath) {
6933 cons_show("Current plugins sourcepath: %s", sourcepath);
6934 g_free(sourcepath);
6935 } else {
6936 cons_show("Plugins sourcepath not currently set.");
6937 }
6938 return TRUE;
6939 }
6940
6941 if (g_strcmp0(args[1], "clear") == 0) {
6942 prefs_set_string(PREF_PLUGINS_SOURCEPATH, NULL);
6943 cons_show("Plugins sourcepath cleared.");
6944 return TRUE;
6945 }
6946
6947 if (g_strcmp0(args[1], "set") == 0) {
6948 if (args[2] == NULL) {
6949 cons_bad_cmd_usage(command);
6950 return TRUE;
6951 }
6952
6953 char* path = get_expanded_path(args[2]);
6954
6955 if (!is_dir(path)) {
6956 cons_show("Plugins sourcepath must be a directory.");
6957 free(path);
6958 return TRUE;
6959 }
6960
6961 cons_show("Setting plugins sourcepath: %s", path);
6962 prefs_set_string(PREF_PLUGINS_SOURCEPATH, path);
6963 free(path);
6964 return TRUE;
6965 }
6966
6967 cons_bad_cmd_usage(command);
6968 return TRUE;
6969 }
6970
6971 gboolean
cmd_plugins_install(ProfWin * window,const char * const command,gchar ** args)6972 cmd_plugins_install(ProfWin* window, const char* const command, gchar** args)
6973 {
6974 char* path;
6975
6976 if (args[1] == NULL) {
6977 char* sourcepath = prefs_get_string(PREF_PLUGINS_SOURCEPATH);
6978 if (sourcepath) {
6979 path = strdup(sourcepath);
6980 g_free(sourcepath);
6981 } else {
6982 cons_show("Either a path must be provided or the sourcepath property must be set, see /help plugins");
6983 return TRUE;
6984 }
6985 } else {
6986 path = get_expanded_path(args[1]);
6987 }
6988
6989 if (is_regular_file(path)) {
6990 if (!g_str_has_suffix(path, ".py") && !g_str_has_suffix(path, ".so")) {
6991 cons_show("Plugins must have one of the following extensions: '.py' '.so'");
6992 free(path);
6993 return TRUE;
6994 }
6995
6996 GString* error_message = g_string_new(NULL);
6997 gchar* plugin_name = g_path_get_basename(path);
6998 gboolean result = plugins_install(plugin_name, path, error_message);
6999 if (result) {
7000 cons_show("Plugin installed: %s", plugin_name);
7001 } else {
7002 cons_show("Failed to install plugin: %s. %s", plugin_name, error_message->str);
7003 }
7004 g_free(plugin_name);
7005 g_string_free(error_message, TRUE);
7006 free(path);
7007 return TRUE;
7008 } else if (is_dir(path)) {
7009 PluginsInstallResult* result = plugins_install_all(path);
7010 if (result->installed || result->failed) {
7011 if (result->installed) {
7012 cons_show("");
7013 cons_show("Installed plugins:");
7014 GSList* curr = result->installed;
7015 while (curr) {
7016 cons_show(" %s", curr->data);
7017 curr = g_slist_next(curr);
7018 }
7019 }
7020 if (result->failed) {
7021 cons_show("");
7022 cons_show("Failed installs:");
7023 GSList* curr = result->failed;
7024 while (curr) {
7025 cons_show(" %s", curr->data);
7026 curr = g_slist_next(curr);
7027 }
7028 }
7029 } else {
7030 cons_show("No plugins found in: %s", path);
7031 }
7032 free(path);
7033 plugins_free_install_result(result);
7034 return TRUE;
7035 } else {
7036 cons_show("Argument must be a file or directory.");
7037 }
7038
7039 free(path);
7040 return TRUE;
7041 }
7042
7043 gboolean
cmd_plugins_update(ProfWin * window,const char * const command,gchar ** args)7044 cmd_plugins_update(ProfWin* window, const char* const command, gchar** args)
7045 {
7046 char* path;
7047
7048 if (args[1] == NULL) {
7049 char* sourcepath = prefs_get_string(PREF_PLUGINS_SOURCEPATH);
7050 if (sourcepath) {
7051 path = strdup(sourcepath);
7052 g_free(sourcepath);
7053 } else {
7054 cons_show("Either a path must be provided or the sourcepath property must be set, see /help plugins");
7055 return TRUE;
7056 }
7057 } else {
7058 path = get_expanded_path(args[1]);
7059 }
7060
7061 if (access(path, R_OK) != 0) {
7062 cons_show("File not found: %s", path);
7063 free(path);
7064 return TRUE;
7065 }
7066
7067 if (is_regular_file(path)) {
7068 if (!g_str_has_suffix(path, ".py") && !g_str_has_suffix(path, ".so")) {
7069 cons_show("Plugins must have one of the following extensions: '.py' '.so'");
7070 free(path);
7071 return TRUE;
7072 }
7073
7074 GString* error_message = g_string_new(NULL);
7075 gchar* plugin_name = g_path_get_basename(path);
7076 if (plugins_unload(plugin_name)) {
7077 if (plugins_uninstall(plugin_name)) {
7078 if (plugins_install(plugin_name, path, error_message)) {
7079 cons_show("Plugin installed: %s", plugin_name);
7080 } else {
7081 cons_show("Failed to install plugin: %s. %s", plugin_name, error_message->str);
7082 }
7083 } else {
7084 cons_show("Failed to uninstall plugin: %s.", plugin_name);
7085 }
7086 } else {
7087 cons_show("Failed to unload plugin: %s.", plugin_name);
7088 }
7089 g_free(plugin_name);
7090 g_string_free(error_message, TRUE);
7091 free(path);
7092 return TRUE;
7093 }
7094
7095 if (is_dir(path)) {
7096 free(path);
7097 return FALSE;
7098 }
7099
7100 free(path);
7101 cons_show("Argument must be a file or directory.");
7102 return TRUE;
7103 }
7104
7105 gboolean
cmd_plugins_uninstall(ProfWin * window,const char * const command,gchar ** args)7106 cmd_plugins_uninstall(ProfWin* window, const char* const command, gchar** args)
7107 {
7108 if (args[1] == NULL) {
7109 return FALSE;
7110 }
7111
7112 gboolean res = plugins_uninstall(args[1]);
7113 if (res) {
7114 cons_show("Uninstalled plugin: %s", args[1]);
7115 } else {
7116 cons_show("Failed to uninstall plugin: %s", args[1]);
7117 }
7118
7119 return TRUE;
7120 }
7121
7122 gboolean
cmd_plugins_load(ProfWin * window,const char * const command,gchar ** args)7123 cmd_plugins_load(ProfWin* window, const char* const command, gchar** args)
7124 {
7125 if (args[1] == NULL) {
7126 GSList* loaded = plugins_load_all();
7127 if (loaded) {
7128 cons_show("Loaded plugins:");
7129 GSList* curr = loaded;
7130 while (curr) {
7131 cons_show(" %s", curr->data);
7132 curr = g_slist_next(curr);
7133 }
7134 g_slist_free_full(loaded, g_free);
7135 } else {
7136 cons_show("No plugins loaded.");
7137 }
7138 return TRUE;
7139 }
7140
7141 GString* error_message = g_string_new(NULL);
7142 gboolean res = plugins_load(args[1], error_message);
7143 if (res) {
7144 cons_show("Loaded plugin: %s", args[1]);
7145 } else {
7146 cons_show("Failed to load plugin: %s. %s", args[1], error_message->str);
7147 }
7148 g_string_free(error_message, TRUE);
7149
7150 return TRUE;
7151 }
7152
7153 gboolean
cmd_plugins_unload(ProfWin * window,const char * const command,gchar ** args)7154 cmd_plugins_unload(ProfWin* window, const char* const command, gchar** args)
7155 {
7156 if (args[1] == NULL) {
7157 gboolean res = plugins_unload_all();
7158 if (res) {
7159 cons_show("Unloaded all plugins.");
7160 } else {
7161 cons_show("No plugins unloaded.");
7162 }
7163 return TRUE;
7164 }
7165
7166 gboolean res = plugins_unload(args[1]);
7167 if (res) {
7168 cons_show("Unloaded plugin: %s", args[1]);
7169 } else {
7170 cons_show("Failed to unload plugin: %s", args[1]);
7171 }
7172
7173 return TRUE;
7174 }
7175
7176 gboolean
cmd_plugins_reload(ProfWin * window,const char * const command,gchar ** args)7177 cmd_plugins_reload(ProfWin* window, const char* const command, gchar** args)
7178 {
7179 if (args[1] == NULL) {
7180 plugins_reload_all();
7181 cons_show("Reloaded all plugins");
7182 return TRUE;
7183 }
7184
7185 GString* error_message = g_string_new(NULL);
7186 gboolean res = plugins_reload(args[1], error_message);
7187 if (res) {
7188 cons_show("Reloaded plugin: %s", args[1]);
7189 } else {
7190 cons_show("Failed to reload plugin: %s, %s", args[1], error_message);
7191 }
7192 g_string_free(error_message, TRUE);
7193
7194 return TRUE;
7195 }
7196
7197 gboolean
cmd_plugins_python_version(ProfWin * window,const char * const command,gchar ** args)7198 cmd_plugins_python_version(ProfWin* window, const char* const command, gchar** args)
7199 {
7200 #ifdef HAVE_PYTHON
7201 const char* version = python_get_version_string();
7202 cons_show("Python version:");
7203 cons_show("%s", version);
7204 #else
7205 cons_show("This build does not support python plugins.");
7206 #endif
7207 return TRUE;
7208 }
7209
7210 gboolean
cmd_plugins(ProfWin * window,const char * const command,gchar ** args)7211 cmd_plugins(ProfWin* window, const char* const command, gchar** args)
7212 {
7213 GList* plugins = plugins_loaded_list();
7214 if (plugins == NULL) {
7215 cons_show("No plugins installed.");
7216 return TRUE;
7217 }
7218
7219 GList* curr = plugins;
7220 cons_show("Installed plugins:");
7221 while (curr) {
7222 cons_show(" %s", curr->data);
7223 curr = g_list_next(curr);
7224 }
7225 g_list_free(plugins);
7226
7227 return TRUE;
7228 }
7229
7230 gboolean
cmd_pgp(ProfWin * window,const char * const command,gchar ** args)7231 cmd_pgp(ProfWin* window, const char* const command, gchar** args)
7232 {
7233 #ifdef HAVE_LIBGPGME
7234 if (args[0] == NULL) {
7235 cons_bad_cmd_usage(command);
7236 return TRUE;
7237 }
7238
7239 if (strcmp(args[0], "char") == 0) {
7240 if (args[1] == NULL) {
7241 cons_bad_cmd_usage(command);
7242 return TRUE;
7243 } else if (g_utf8_strlen(args[1], 4) == 1) {
7244 if (prefs_set_pgp_char(args[1])) {
7245 cons_show("PGP char set to %s.", args[1]);
7246 } else {
7247 cons_show_error("Could not set PGP char: %s.", args[1]);
7248 }
7249 return TRUE;
7250 }
7251 cons_bad_cmd_usage(command);
7252 return TRUE;
7253 } else if (g_strcmp0(args[0], "log") == 0) {
7254 char* choice = args[1];
7255 if (g_strcmp0(choice, "on") == 0) {
7256 prefs_set_string(PREF_PGP_LOG, "on");
7257 cons_show("PGP messages will be logged as plaintext.");
7258 if (!prefs_get_boolean(PREF_CHLOG)) {
7259 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
7260 }
7261 } else if (g_strcmp0(choice, "off") == 0) {
7262 prefs_set_string(PREF_PGP_LOG, "off");
7263 cons_show("PGP message logging disabled.");
7264 } else if (g_strcmp0(choice, "redact") == 0) {
7265 prefs_set_string(PREF_PGP_LOG, "redact");
7266 cons_show("PGP messages will be logged as '[redacted]'.");
7267 if (!prefs_get_boolean(PREF_CHLOG)) {
7268 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
7269 }
7270 } else {
7271 cons_bad_cmd_usage(command);
7272 }
7273 return TRUE;
7274 }
7275
7276 if (g_strcmp0(args[0], "keys") == 0) {
7277 GHashTable* keys = p_gpg_list_keys();
7278 if (!keys || g_hash_table_size(keys) == 0) {
7279 cons_show("No keys found");
7280 return TRUE;
7281 }
7282
7283 cons_show("PGP keys:");
7284 GList* keylist = g_hash_table_get_keys(keys);
7285 GList* curr = keylist;
7286 while (curr) {
7287 ProfPGPKey* key = g_hash_table_lookup(keys, curr->data);
7288 cons_show(" %s", key->name);
7289 cons_show(" ID : %s", key->id);
7290 char* format_fp = p_gpg_format_fp_str(key->fp);
7291 cons_show(" Fingerprint : %s", format_fp);
7292 free(format_fp);
7293 if (key->secret) {
7294 cons_show(" Type : PUBLIC, PRIVATE");
7295 } else {
7296 cons_show(" Type : PUBLIC");
7297 }
7298 curr = g_list_next(curr);
7299 }
7300 g_list_free(keylist);
7301 p_gpg_free_keys(keys);
7302 return TRUE;
7303 }
7304
7305 if (g_strcmp0(args[0], "setkey") == 0) {
7306 jabber_conn_status_t conn_status = connection_get_status();
7307 if (conn_status != JABBER_CONNECTED) {
7308 cons_show("You are not currently connected.");
7309 return TRUE;
7310 }
7311
7312 char* jid = args[1];
7313 if (!args[1]) {
7314 cons_bad_cmd_usage(command);
7315 return TRUE;
7316 }
7317
7318 char* keyid = args[2];
7319 if (!args[2]) {
7320 cons_bad_cmd_usage(command);
7321 return TRUE;
7322 }
7323
7324 gboolean res = p_gpg_addkey(jid, keyid);
7325 if (!res) {
7326 cons_show("Key ID not found.");
7327 } else {
7328 cons_show("Key %s set for %s.", keyid, jid);
7329 }
7330
7331 return TRUE;
7332 }
7333
7334 if (g_strcmp0(args[0], "contacts") == 0) {
7335 jabber_conn_status_t conn_status = connection_get_status();
7336 if (conn_status != JABBER_CONNECTED) {
7337 cons_show("You are not currently connected.");
7338 return TRUE;
7339 }
7340 GHashTable* pubkeys = p_gpg_pubkeys();
7341 GList* jids = g_hash_table_get_keys(pubkeys);
7342 if (!jids) {
7343 cons_show("No contacts found with PGP public keys assigned.");
7344 return TRUE;
7345 }
7346
7347 cons_show("Assigned PGP public keys:");
7348 GList* curr = jids;
7349 while (curr) {
7350 char* jid = curr->data;
7351 ProfPGPPubKeyId* pubkeyid = g_hash_table_lookup(pubkeys, jid);
7352 if (pubkeyid->received) {
7353 cons_show(" %s: %s (received)", jid, pubkeyid->id);
7354 } else {
7355 cons_show(" %s: %s (stored)", jid, pubkeyid->id);
7356 }
7357 curr = g_list_next(curr);
7358 }
7359 g_list_free(jids);
7360 return TRUE;
7361 }
7362
7363 if (g_strcmp0(args[0], "libver") == 0) {
7364 const char* libver = p_gpg_libver();
7365 if (!libver) {
7366 cons_show("Could not get libgpgme version");
7367 return TRUE;
7368 }
7369
7370 GString* fullstr = g_string_new("Using libgpgme version ");
7371 g_string_append(fullstr, libver);
7372 cons_show("%s", fullstr->str);
7373 g_string_free(fullstr, TRUE);
7374
7375 return TRUE;
7376 }
7377
7378 if (g_strcmp0(args[0], "start") == 0) {
7379 jabber_conn_status_t conn_status = connection_get_status();
7380 if (conn_status != JABBER_CONNECTED) {
7381 cons_show("You must be connected to start PGP encrpytion.");
7382 return TRUE;
7383 }
7384
7385 if (window->type != WIN_CHAT && args[1] == NULL) {
7386 cons_show("You must be in a regular chat window to start PGP encrpytion.");
7387 return TRUE;
7388 }
7389
7390 ProfChatWin* chatwin = NULL;
7391
7392 if (args[1]) {
7393 char* contact = args[1];
7394 char* barejid = roster_barejid_from_name(contact);
7395 if (barejid == NULL) {
7396 barejid = contact;
7397 }
7398
7399 chatwin = wins_get_chat(barejid);
7400 if (!chatwin) {
7401 chatwin = chatwin_new(barejid);
7402 }
7403 ui_focus_win((ProfWin*)chatwin);
7404 } else {
7405 chatwin = (ProfChatWin*)window;
7406 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7407 }
7408
7409 if (chatwin->is_otr) {
7410 win_println(window, THEME_DEFAULT, "!", "You must end the OTR session to start PGP encryption.");
7411 return TRUE;
7412 }
7413
7414 if (chatwin->pgp_send) {
7415 win_println(window, THEME_DEFAULT, "!", "You have already started PGP encryption.");
7416 return TRUE;
7417 }
7418
7419 if (chatwin->is_omemo) {
7420 win_println(window, THEME_DEFAULT, "!", "You must disable OMEMO before starting an PGP encrypted session.");
7421 return TRUE;
7422 }
7423
7424 ProfAccount* account = accounts_get_account(session_get_account_name());
7425 char* err_str = NULL;
7426 if (!p_gpg_valid_key(account->pgp_keyid, &err_str)) {
7427 win_println(window, THEME_DEFAULT, "!", "Invalid PGP key ID %s: %s, cannot start PGP encryption.", account->pgp_keyid, err_str);
7428 free(err_str);
7429 account_free(account);
7430 return TRUE;
7431 }
7432 free(err_str);
7433 account_free(account);
7434
7435 if (!p_gpg_available(chatwin->barejid)) {
7436 win_println(window, THEME_DEFAULT, "!", "No PGP key found for %s.", chatwin->barejid);
7437 return TRUE;
7438 }
7439
7440 chatwin->pgp_send = TRUE;
7441 win_println(window, THEME_DEFAULT, "!", "PGP encryption enabled.");
7442 return TRUE;
7443 }
7444
7445 if (g_strcmp0(args[0], "end") == 0) {
7446 jabber_conn_status_t conn_status = connection_get_status();
7447 if (conn_status != JABBER_CONNECTED) {
7448 cons_show("You are not currently connected.");
7449 return TRUE;
7450 }
7451
7452 if (window->type != WIN_CHAT) {
7453 cons_show("You must be in a regular chat window to end PGP encrpytion.");
7454 return TRUE;
7455 }
7456
7457 ProfChatWin* chatwin = (ProfChatWin*)window;
7458 if (chatwin->pgp_send == FALSE) {
7459 win_println(window, THEME_DEFAULT, "!", "PGP encryption is not currently enabled.");
7460 return TRUE;
7461 }
7462
7463 chatwin->pgp_send = FALSE;
7464 win_println(window, THEME_DEFAULT, "!", "PGP encryption disabled.");
7465 return TRUE;
7466 }
7467
7468 if (g_strcmp0(args[0], "sendfile") == 0) {
7469 _cmd_set_boolean_preference(args[1], command, "Sending unencrypted files using /sendfile while otherwise using PGP", PREF_PGP_SENDFILE);
7470 return TRUE;
7471 }
7472
7473 cons_bad_cmd_usage(command);
7474 return TRUE;
7475 #else
7476 cons_show("This version of Profanity has not been built with PGP support enabled");
7477 return TRUE;
7478 #endif
7479 }
7480
7481 #ifdef HAVE_LIBGPGME
7482
7483 /*!
7484 * \brief Command for XEP-0373: OpenPGP for XMPP
7485 *
7486 */
7487
7488 gboolean
cmd_ox(ProfWin * window,const char * const command,gchar ** args)7489 cmd_ox(ProfWin* window, const char* const command, gchar** args)
7490 {
7491 if (args[0] == NULL) {
7492 cons_bad_cmd_usage(command);
7493 return TRUE;
7494 }
7495
7496 if (strcmp(args[0], "char") == 0) {
7497 if (args[1] == NULL) {
7498 cons_bad_cmd_usage(command);
7499 return TRUE;
7500 } else if (g_utf8_strlen(args[1], 4) == 1) {
7501 if (prefs_set_ox_char(args[1])) {
7502 cons_show("OX char set to %s.", args[1]);
7503 } else {
7504 cons_show_error("Could not set OX char: %s.", args[1]);
7505 }
7506 return TRUE;
7507 }
7508 cons_bad_cmd_usage(command);
7509 return TRUE;
7510 }
7511
7512 // The '/ox keys' command - same like in pgp
7513 // Should we move this to a common command
7514 // e.g. '/openpgp keys'?.
7515 else if (g_strcmp0(args[0], "keys") == 0) {
7516 GHashTable* keys = p_gpg_list_keys();
7517 if (!keys || g_hash_table_size(keys) == 0) {
7518 cons_show("No keys found");
7519 return TRUE;
7520 }
7521
7522 cons_show("OpenPGP keys:");
7523 GList* keylist = g_hash_table_get_keys(keys);
7524 GList* curr = keylist;
7525 while (curr) {
7526 ProfPGPKey* key = g_hash_table_lookup(keys, curr->data);
7527 cons_show(" %s", key->name);
7528 cons_show(" ID : %s", key->id);
7529 char* format_fp = p_gpg_format_fp_str(key->fp);
7530 cons_show(" Fingerprint : %s", format_fp);
7531 free(format_fp);
7532 if (key->secret) {
7533 cons_show(" Type : PUBLIC, PRIVATE");
7534 } else {
7535 cons_show(" Type : PUBLIC");
7536 }
7537 curr = g_list_next(curr);
7538 }
7539 g_list_free(keylist);
7540 p_gpg_free_keys(keys);
7541 return TRUE;
7542 }
7543
7544 else if (g_strcmp0(args[0], "contacts") == 0) {
7545 GHashTable* keys = ox_gpg_public_keys();
7546 cons_show("OpenPGP keys:");
7547 GList* keylist = g_hash_table_get_keys(keys);
7548 GList* curr = keylist;
7549
7550 GSList* roster_list = NULL;
7551 jabber_conn_status_t conn_status = connection_get_status();
7552 if (conn_status != JABBER_CONNECTED) {
7553 cons_show("You are not currently connected.");
7554 } else {
7555 roster_list = roster_get_contacts(ROSTER_ORD_NAME);
7556 }
7557
7558 while (curr) {
7559 ProfPGPKey* key = g_hash_table_lookup(keys, curr->data);
7560 PContact contact = NULL;
7561 if (roster_list) {
7562 GSList* curr_c = roster_list;
7563 while (!contact && curr_c) {
7564 contact = curr_c->data;
7565 const char* jid = p_contact_barejid(contact);
7566 GString* xmppuri = g_string_new("xmpp:");
7567 g_string_append(xmppuri, jid);
7568 if (g_strcmp0(key->name, xmppuri->str)) {
7569 contact = NULL;
7570 }
7571 curr_c = g_slist_next(curr_c);
7572 }
7573 }
7574
7575 if (contact) {
7576 cons_show("%s - %s", key->fp, key->name);
7577 } else {
7578 cons_show("%s - %s (not in roster)", key->fp, key->name);
7579 }
7580 curr = g_list_next(curr);
7581 }
7582
7583 } else if (g_strcmp0(args[0], "start") == 0) {
7584 jabber_conn_status_t conn_status = connection_get_status();
7585 if (conn_status != JABBER_CONNECTED) {
7586 cons_show("You must be connected to start OX encrpytion.");
7587 return TRUE;
7588 }
7589
7590 if (window->type != WIN_CHAT && args[1] == NULL) {
7591 cons_show("You must be in a regular chat window to start OX encrpytion.");
7592 return TRUE;
7593 }
7594
7595 ProfChatWin* chatwin = NULL;
7596
7597 if (args[1]) {
7598 char* contact = args[1];
7599 char* barejid = roster_barejid_from_name(contact);
7600 if (barejid == NULL) {
7601 barejid = contact;
7602 }
7603
7604 chatwin = wins_get_chat(barejid);
7605 if (!chatwin) {
7606 chatwin = chatwin_new(barejid);
7607 }
7608 ui_focus_win((ProfWin*)chatwin);
7609 } else {
7610 chatwin = (ProfChatWin*)window;
7611 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7612 }
7613
7614 if (chatwin->is_otr) {
7615 win_println(window, THEME_DEFAULT, "!", "You must end the OTR session to start OX encryption.");
7616 return TRUE;
7617 }
7618
7619 if (chatwin->pgp_send) {
7620 win_println(window, THEME_DEFAULT, "!", "You must end the PGP session to start OX encryption.");
7621 return TRUE;
7622 }
7623
7624 if (chatwin->is_ox) {
7625 win_println(window, THEME_DEFAULT, "!", "You have already started OX encryption.");
7626 return TRUE;
7627 }
7628
7629 ProfAccount* account = accounts_get_account(session_get_account_name());
7630
7631 if (!ox_is_private_key_available(account->jid)) {
7632 win_println(window, THEME_DEFAULT, "!", "No private OpenPGP found, cannot start OX encryption.");
7633 account_free(account);
7634 return TRUE;
7635 }
7636 account_free(account);
7637
7638 if (!ox_is_public_key_available(chatwin->barejid)) {
7639 win_println(window, THEME_DEFAULT, "!", "No OX-OpenPGP key found for %s.", chatwin->barejid);
7640 return TRUE;
7641 }
7642
7643 chatwin->is_ox = TRUE;
7644 win_println(window, THEME_DEFAULT, "!", "OX encryption enabled.");
7645 return TRUE;
7646 } else if (g_strcmp0(args[0], "announce") == 0) {
7647 if (args[1]) {
7648 ox_announce_public_key(args[1]);
7649 } else {
7650 cons_show("Filename is required");
7651 }
7652 } else if (g_strcmp0(args[0], "discover") == 0) {
7653 if (args[1]) {
7654 ox_discover_public_key(args[1]);
7655 } else {
7656 cons_show("To discover the OpenPGP keys of an user, the JID is required");
7657 }
7658 } else if (g_strcmp0(args[0], "request") == 0) {
7659 if (args[1] && args[2]) {
7660 ox_request_public_key(args[1], args[2]);
7661 } else {
7662 cons_show("JID and OpenPGP Key ID are required");
7663 }
7664 } else {
7665 cons_show("OX not implemented");
7666 }
7667 return TRUE;
7668 }
7669 #endif // HAVE_LIBGPGME
7670
7671 gboolean
cmd_otr_char(ProfWin * window,const char * const command,gchar ** args)7672 cmd_otr_char(ProfWin* window, const char* const command, gchar** args)
7673 {
7674 #ifdef HAVE_LIBOTR
7675 if (args[1] == NULL) {
7676 cons_bad_cmd_usage(command);
7677 return TRUE;
7678 } else if (g_utf8_strlen(args[1], 4) == 1) {
7679 if (prefs_set_otr_char(args[1])) {
7680 cons_show("OTR char set to %s.", args[1]);
7681 } else {
7682 cons_show_error("Could not set OTR char: %s.", args[1]);
7683 }
7684 return TRUE;
7685 }
7686 cons_bad_cmd_usage(command);
7687 #else
7688 cons_show("This version of Profanity has not been built with OTR support enabled");
7689 #endif
7690 return TRUE;
7691 }
7692
7693 gboolean
cmd_otr_log(ProfWin * window,const char * const command,gchar ** args)7694 cmd_otr_log(ProfWin* window, const char* const command, gchar** args)
7695 {
7696 #ifdef HAVE_LIBOTR
7697 char* choice = args[1];
7698 if (g_strcmp0(choice, "on") == 0) {
7699 prefs_set_string(PREF_OTR_LOG, "on");
7700 cons_show("OTR messages will be logged as plaintext.");
7701 if (!prefs_get_boolean(PREF_CHLOG)) {
7702 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
7703 }
7704 } else if (g_strcmp0(choice, "off") == 0) {
7705 prefs_set_string(PREF_OTR_LOG, "off");
7706 cons_show("OTR message logging disabled.");
7707 } else if (g_strcmp0(choice, "redact") == 0) {
7708 prefs_set_string(PREF_OTR_LOG, "redact");
7709 cons_show("OTR messages will be logged as '[redacted]'.");
7710 if (!prefs_get_boolean(PREF_CHLOG)) {
7711 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
7712 }
7713 } else {
7714 cons_bad_cmd_usage(command);
7715 }
7716 return TRUE;
7717 #else
7718 cons_show("This version of Profanity has not been built with OTR support enabled");
7719 return TRUE;
7720 #endif
7721 }
7722
7723 gboolean
cmd_otr_libver(ProfWin * window,const char * const command,gchar ** args)7724 cmd_otr_libver(ProfWin* window, const char* const command, gchar** args)
7725 {
7726 #ifdef HAVE_LIBOTR
7727 char* version = otr_libotr_version();
7728 cons_show("Using libotr version %s", version);
7729 return TRUE;
7730 #else
7731 cons_show("This version of Profanity has not been built with OTR support enabled");
7732 return TRUE;
7733 #endif
7734 }
7735
7736 gboolean
cmd_otr_policy(ProfWin * window,const char * const command,gchar ** args)7737 cmd_otr_policy(ProfWin* window, const char* const command, gchar** args)
7738 {
7739 #ifdef HAVE_LIBOTR
7740 if (args[1] == NULL) {
7741 char* policy = prefs_get_string(PREF_OTR_POLICY);
7742 cons_show("OTR policy is now set to: %s", policy);
7743 g_free(policy);
7744 return TRUE;
7745 }
7746
7747 char* choice = args[1];
7748 if ((g_strcmp0(choice, "manual") != 0) && (g_strcmp0(choice, "opportunistic") != 0) && (g_strcmp0(choice, "always") != 0)) {
7749 cons_show("OTR policy can be set to: manual, opportunistic or always.");
7750 return TRUE;
7751 }
7752
7753 char* contact = args[2];
7754 if (contact == NULL) {
7755 prefs_set_string(PREF_OTR_POLICY, choice);
7756 cons_show("OTR policy is now set to: %s", choice);
7757 return TRUE;
7758 }
7759
7760 if (connection_get_status() != JABBER_CONNECTED) {
7761 cons_show("You must be connected to set the OTR policy for a contact.");
7762 return TRUE;
7763 }
7764
7765 char* contact_jid = roster_barejid_from_name(contact);
7766 if (contact_jid == NULL) {
7767 contact_jid = contact;
7768 }
7769 accounts_add_otr_policy(session_get_account_name(), contact_jid, choice);
7770 cons_show("OTR policy for %s set to: %s", contact_jid, choice);
7771 return TRUE;
7772 #else
7773 cons_show("This version of Profanity has not been built with OTR support enabled");
7774 return TRUE;
7775 #endif
7776 }
7777
7778 gboolean
cmd_otr_gen(ProfWin * window,const char * const command,gchar ** args)7779 cmd_otr_gen(ProfWin* window, const char* const command, gchar** args)
7780 {
7781 #ifdef HAVE_LIBOTR
7782 if (connection_get_status() != JABBER_CONNECTED) {
7783 cons_show("You must be connected with an account to load OTR information.");
7784 return TRUE;
7785 }
7786
7787 ProfAccount* account = accounts_get_account(session_get_account_name());
7788 otr_keygen(account);
7789 account_free(account);
7790 return TRUE;
7791 #else
7792 cons_show("This version of Profanity has not been built with OTR support enabled");
7793 return TRUE;
7794 #endif
7795 }
7796
7797 gboolean
cmd_otr_myfp(ProfWin * window,const char * const command,gchar ** args)7798 cmd_otr_myfp(ProfWin* window, const char* const command, gchar** args)
7799 {
7800 #ifdef HAVE_LIBOTR
7801 if (connection_get_status() != JABBER_CONNECTED) {
7802 cons_show("You must be connected with an account to load OTR information.");
7803 return TRUE;
7804 }
7805
7806 if (!otr_key_loaded()) {
7807 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a private key, use '/otr gen'");
7808 return TRUE;
7809 }
7810
7811 char* fingerprint = otr_get_my_fingerprint();
7812 win_println(window, THEME_DEFAULT, "!", "Your OTR fingerprint: %s", fingerprint);
7813 free(fingerprint);
7814 return TRUE;
7815 #else
7816 cons_show("This version of Profanity has not been built with OTR support enabled");
7817 return TRUE;
7818 #endif
7819 }
7820
7821 gboolean
cmd_otr_theirfp(ProfWin * window,const char * const command,gchar ** args)7822 cmd_otr_theirfp(ProfWin* window, const char* const command, gchar** args)
7823 {
7824 #ifdef HAVE_LIBOTR
7825 if (connection_get_status() != JABBER_CONNECTED) {
7826 cons_show("You must be connected with an account to load OTR information.");
7827 return TRUE;
7828 }
7829
7830 if (window->type != WIN_CHAT) {
7831 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to view a recipient's fingerprint.");
7832 return TRUE;
7833 }
7834
7835 ProfChatWin* chatwin = (ProfChatWin*)window;
7836 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7837 if (chatwin->is_otr == FALSE) {
7838 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
7839 return TRUE;
7840 }
7841
7842 char* fingerprint = otr_get_their_fingerprint(chatwin->barejid);
7843 win_println(window, THEME_DEFAULT, "!", "%s's OTR fingerprint: %s", chatwin->barejid, fingerprint);
7844 free(fingerprint);
7845 return TRUE;
7846 #else
7847 cons_show("This version of Profanity has not been built with OTR support enabled");
7848 return TRUE;
7849 #endif
7850 }
7851
7852 gboolean
cmd_otr_start(ProfWin * window,const char * const command,gchar ** args)7853 cmd_otr_start(ProfWin* window, const char* const command, gchar** args)
7854 {
7855 #ifdef HAVE_LIBOTR
7856 if (connection_get_status() != JABBER_CONNECTED) {
7857 cons_show("You must be connected with an account to load OTR information.");
7858 return TRUE;
7859 }
7860
7861 // recipient supplied
7862 if (args[1]) {
7863 char* contact = args[1];
7864 char* barejid = roster_barejid_from_name(contact);
7865 if (barejid == NULL) {
7866 barejid = contact;
7867 }
7868
7869 ProfChatWin* chatwin = wins_get_chat(barejid);
7870 if (!chatwin) {
7871 chatwin = chatwin_new(barejid);
7872 }
7873 ui_focus_win((ProfWin*)chatwin);
7874
7875 if (chatwin->pgp_send) {
7876 win_println(window, THEME_DEFAULT, "!", "You must disable PGP encryption before starting an OTR session.");
7877 return TRUE;
7878 }
7879
7880 if (chatwin->is_omemo) {
7881 win_println(window, THEME_DEFAULT, "!", "You must disable OMEMO before starting an OTR session.");
7882 return TRUE;
7883 }
7884
7885 if (chatwin->is_otr) {
7886 win_println(window, THEME_DEFAULT, "!", "You are already in an OTR session.");
7887 return TRUE;
7888 }
7889
7890 if (!otr_key_loaded()) {
7891 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a private key, use '/otr gen'");
7892 return TRUE;
7893 }
7894
7895 if (!otr_is_secure(barejid)) {
7896 char* otr_query_message = otr_start_query();
7897 char* id = message_send_chat_otr(barejid, otr_query_message, FALSE, NULL);
7898 free(id);
7899 return TRUE;
7900 }
7901
7902 chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
7903 return TRUE;
7904
7905 // no recipient, use current chat
7906 } else {
7907 if (window->type != WIN_CHAT) {
7908 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to start an OTR session.");
7909 return TRUE;
7910 }
7911
7912 ProfChatWin* chatwin = (ProfChatWin*)window;
7913 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7914 if (chatwin->pgp_send) {
7915 win_println(window, THEME_DEFAULT, "!", "You must disable PGP encryption before starting an OTR session.");
7916 return TRUE;
7917 }
7918
7919 if (chatwin->is_otr) {
7920 win_println(window, THEME_DEFAULT, "!", "You are already in an OTR session.");
7921 return TRUE;
7922 }
7923
7924 if (!otr_key_loaded()) {
7925 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a private key, use '/otr gen'");
7926 return TRUE;
7927 }
7928
7929 char* otr_query_message = otr_start_query();
7930 char* id = message_send_chat_otr(chatwin->barejid, otr_query_message, FALSE, NULL);
7931
7932 free(id);
7933 return TRUE;
7934 }
7935 #else
7936 cons_show("This version of Profanity has not been built with OTR support enabled");
7937 return TRUE;
7938 #endif
7939 }
7940
7941 gboolean
cmd_otr_end(ProfWin * window,const char * const command,gchar ** args)7942 cmd_otr_end(ProfWin* window, const char* const command, gchar** args)
7943 {
7944 #ifdef HAVE_LIBOTR
7945 if (connection_get_status() != JABBER_CONNECTED) {
7946 cons_show("You must be connected with an account to load OTR information.");
7947 return TRUE;
7948 }
7949
7950 if (window->type != WIN_CHAT) {
7951 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to use OTR.");
7952 return TRUE;
7953 }
7954
7955 ProfChatWin* chatwin = (ProfChatWin*)window;
7956 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7957 if (chatwin->is_otr == FALSE) {
7958 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
7959 return TRUE;
7960 }
7961
7962 chatwin_otr_unsecured(chatwin);
7963 otr_end_session(chatwin->barejid);
7964 return TRUE;
7965 #else
7966 cons_show("This version of Profanity has not been built with OTR support enabled");
7967 return TRUE;
7968 #endif
7969 }
7970
7971 gboolean
cmd_otr_trust(ProfWin * window,const char * const command,gchar ** args)7972 cmd_otr_trust(ProfWin* window, const char* const command, gchar** args)
7973 {
7974 #ifdef HAVE_LIBOTR
7975 if (connection_get_status() != JABBER_CONNECTED) {
7976 cons_show("You must be connected with an account to load OTR information.");
7977 return TRUE;
7978 }
7979
7980 if (window->type != WIN_CHAT) {
7981 win_println(window, THEME_DEFAULT, "-", "You must be in an OTR session to trust a recipient.");
7982 return TRUE;
7983 }
7984
7985 ProfChatWin* chatwin = (ProfChatWin*)window;
7986 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
7987 if (chatwin->is_otr == FALSE) {
7988 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
7989 return TRUE;
7990 }
7991
7992 chatwin_otr_trust(chatwin);
7993 otr_trust(chatwin->barejid);
7994 return TRUE;
7995 #else
7996 cons_show("This version of Profanity has not been built with OTR support enabled");
7997 return TRUE;
7998 #endif
7999 }
8000
8001 gboolean
cmd_otr_untrust(ProfWin * window,const char * const command,gchar ** args)8002 cmd_otr_untrust(ProfWin* window, const char* const command, gchar** args)
8003 {
8004 #ifdef HAVE_LIBOTR
8005 if (connection_get_status() != JABBER_CONNECTED) {
8006 cons_show("You must be connected with an account to load OTR information.");
8007 return TRUE;
8008 }
8009
8010 if (window->type != WIN_CHAT) {
8011 win_println(window, THEME_DEFAULT, "-", "You must be in an OTR session to untrust a recipient.");
8012 return TRUE;
8013 }
8014
8015 ProfChatWin* chatwin = (ProfChatWin*)window;
8016 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8017 if (chatwin->is_otr == FALSE) {
8018 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
8019 return TRUE;
8020 }
8021
8022 chatwin_otr_untrust(chatwin);
8023 otr_untrust(chatwin->barejid);
8024 return TRUE;
8025 #else
8026 cons_show("This version of Profanity has not been built with OTR support enabled");
8027 return TRUE;
8028 #endif
8029 }
8030
8031 gboolean
cmd_otr_secret(ProfWin * window,const char * const command,gchar ** args)8032 cmd_otr_secret(ProfWin* window, const char* const command, gchar** args)
8033 {
8034 #ifdef HAVE_LIBOTR
8035 if (connection_get_status() != JABBER_CONNECTED) {
8036 cons_show("You must be connected with an account to load OTR information.");
8037 return TRUE;
8038 }
8039
8040 if (window->type != WIN_CHAT) {
8041 win_println(window, THEME_DEFAULT, "-", "You must be in an OTR session to trust a recipient.");
8042 return TRUE;
8043 }
8044
8045 ProfChatWin* chatwin = (ProfChatWin*)window;
8046 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8047 if (chatwin->is_otr == FALSE) {
8048 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
8049 return TRUE;
8050 }
8051
8052 char* secret = args[1];
8053 if (secret == NULL) {
8054 cons_bad_cmd_usage(command);
8055 return TRUE;
8056 }
8057
8058 otr_smp_secret(chatwin->barejid, secret);
8059 return TRUE;
8060 #else
8061 cons_show("This version of Profanity has not been built with OTR support enabled");
8062 return TRUE;
8063 #endif
8064 }
8065
8066 gboolean
cmd_otr_question(ProfWin * window,const char * const command,gchar ** args)8067 cmd_otr_question(ProfWin* window, const char* const command, gchar** args)
8068 {
8069 #ifdef HAVE_LIBOTR
8070 if (connection_get_status() != JABBER_CONNECTED) {
8071 cons_show("You must be connected with an account to load OTR information.");
8072 return TRUE;
8073 }
8074
8075 char* question = args[1];
8076 char* answer = args[2];
8077 if (question == NULL || answer == NULL) {
8078 cons_bad_cmd_usage(command);
8079 return TRUE;
8080 }
8081
8082 if (window->type != WIN_CHAT) {
8083 win_println(window, THEME_DEFAULT, "-", "You must be in an OTR session to trust a recipient.");
8084 return TRUE;
8085 }
8086
8087 ProfChatWin* chatwin = (ProfChatWin*)window;
8088 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8089 if (chatwin->is_otr == FALSE) {
8090 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
8091 return TRUE;
8092 }
8093
8094 otr_smp_question(chatwin->barejid, question, answer);
8095 return TRUE;
8096 #else
8097 cons_show("This version of Profanity has not been built with OTR support enabled");
8098 return TRUE;
8099 #endif
8100 }
8101
8102 gboolean
cmd_otr_answer(ProfWin * window,const char * const command,gchar ** args)8103 cmd_otr_answer(ProfWin* window, const char* const command, gchar** args)
8104 {
8105 #ifdef HAVE_LIBOTR
8106 if (connection_get_status() != JABBER_CONNECTED) {
8107 cons_show("You must be connected with an account to load OTR information.");
8108 return TRUE;
8109 }
8110
8111 if (window->type != WIN_CHAT) {
8112 win_println(window, THEME_DEFAULT, "-", "You must be in an OTR session to trust a recipient.");
8113 return TRUE;
8114 }
8115
8116 ProfChatWin* chatwin = (ProfChatWin*)window;
8117 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8118 if (chatwin->is_otr == FALSE) {
8119 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OTR session.");
8120 return TRUE;
8121 }
8122
8123 char* answer = args[1];
8124 if (answer == NULL) {
8125 cons_bad_cmd_usage(command);
8126 return TRUE;
8127 }
8128
8129 otr_smp_answer(chatwin->barejid, answer);
8130 return TRUE;
8131 #else
8132 cons_show("This version of Profanity has not been built with OTR support enabled");
8133 return TRUE;
8134 #endif
8135 }
8136
8137 gboolean
cmd_otr_sendfile(ProfWin * window,const char * const command,gchar ** args)8138 cmd_otr_sendfile(ProfWin* window, const char* const command, gchar** args)
8139 {
8140 #ifdef HAVE_LIBOTR
8141 _cmd_set_boolean_preference(args[1], command, "Sending unencrypted files in an OTR session via /sendfile", PREF_OTR_SENDFILE);
8142
8143 return TRUE;
8144 #else
8145 cons_show("This version of Profanity has not been built with OTR support enabled");
8146 return TRUE;
8147 #endif
8148 }
8149
8150 gboolean
cmd_command_list(ProfWin * window,const char * const command,gchar ** args)8151 cmd_command_list(ProfWin* window, const char* const command, gchar** args)
8152 {
8153 jabber_conn_status_t conn_status = connection_get_status();
8154
8155 if (conn_status != JABBER_CONNECTED) {
8156 cons_show("You are not currently connected.");
8157 return TRUE;
8158 }
8159
8160 if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) {
8161 cons_show("Server does not support ad hoc commands (%s).", XMPP_FEATURE_COMMANDS);
8162 return TRUE;
8163 }
8164
8165 char* jid = args[1];
8166 if (jid == NULL) {
8167 switch (window->type) {
8168 case WIN_MUC:
8169 {
8170 ProfMucWin* mucwin = (ProfMucWin*)window;
8171 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8172 jid = mucwin->roomjid;
8173 break;
8174 }
8175 case WIN_CHAT:
8176 {
8177 ProfChatWin* chatwin = (ProfChatWin*)window;
8178 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8179 jid = chatwin->barejid;
8180 break;
8181 }
8182 case WIN_PRIVATE:
8183 {
8184 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
8185 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
8186 jid = privatewin->fulljid;
8187 break;
8188 }
8189 case WIN_CONSOLE:
8190 {
8191 jid = connection_get_domain();
8192 break;
8193 }
8194 default:
8195 cons_show("Cannot send ad hoc commands.");
8196 return TRUE;
8197 }
8198 }
8199
8200 iq_command_list(jid);
8201
8202 cons_show("List available ad hoc commands");
8203 return TRUE;
8204 }
8205
8206 gboolean
cmd_command_exec(ProfWin * window,const char * const command,gchar ** args)8207 cmd_command_exec(ProfWin* window, const char* const command, gchar** args)
8208 {
8209 jabber_conn_status_t conn_status = connection_get_status();
8210
8211 if (conn_status != JABBER_CONNECTED) {
8212 cons_show("You are not currently connected.");
8213 return TRUE;
8214 }
8215
8216 if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) {
8217 cons_show("Server does not support ad hoc commands (%s).", XMPP_FEATURE_COMMANDS);
8218 return TRUE;
8219 }
8220
8221 if (args[1] == NULL) {
8222 cons_bad_cmd_usage(command);
8223 return TRUE;
8224 }
8225
8226 char* jid = args[2];
8227 if (jid == NULL) {
8228 switch (window->type) {
8229 case WIN_MUC:
8230 {
8231 ProfMucWin* mucwin = (ProfMucWin*)window;
8232 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8233 jid = mucwin->roomjid;
8234 break;
8235 }
8236 case WIN_CHAT:
8237 {
8238 ProfChatWin* chatwin = (ProfChatWin*)window;
8239 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8240 jid = chatwin->barejid;
8241 break;
8242 }
8243 case WIN_PRIVATE:
8244 {
8245 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
8246 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
8247 jid = privatewin->fulljid;
8248 break;
8249 }
8250 case WIN_CONSOLE:
8251 {
8252 jid = connection_get_domain();
8253 break;
8254 }
8255 default:
8256 cons_show("Cannot send ad hoc commands.");
8257 return TRUE;
8258 }
8259 }
8260
8261 iq_command_exec(jid, args[1]);
8262
8263 cons_show("Execute %s...", args[1]);
8264 return TRUE;
8265 }
8266
8267 static gboolean
_cmd_execute(ProfWin * window,const char * const command,const char * const inp)8268 _cmd_execute(ProfWin* window, const char* const command, const char* const inp)
8269 {
8270 if (g_str_has_prefix(command, "/field") && window->type == WIN_CONFIG) {
8271 gboolean result = FALSE;
8272 gchar** args = parse_args_with_freetext(inp, 1, 2, &result);
8273 if (!result) {
8274 win_println(window, THEME_DEFAULT, "!", "Invalid command, see /form help");
8275 result = TRUE;
8276 } else {
8277 gchar** tokens = g_strsplit(inp, " ", 2);
8278 char* field = tokens[0] + 1;
8279 result = cmd_form_field(window, field, args);
8280 g_strfreev(tokens);
8281 }
8282
8283 g_strfreev(args);
8284 return result;
8285 }
8286
8287 Command* cmd = cmd_get(command);
8288 gboolean result = FALSE;
8289
8290 if (cmd) {
8291 gchar** args = cmd->parser(inp, cmd->min_args, cmd->max_args, &result);
8292 if (result == FALSE) {
8293 ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
8294 return TRUE;
8295 }
8296 if (args[0] && cmd->sub_funcs[0][0]) {
8297 int i = 0;
8298 while (cmd->sub_funcs[i][0]) {
8299 if (g_strcmp0(args[0], (char*)cmd->sub_funcs[i][0]) == 0) {
8300 gboolean (*func)(ProfWin * window, const char* const command, gchar** args) = cmd->sub_funcs[i][1];
8301 gboolean result = func(window, command, args);
8302 g_strfreev(args);
8303 return result;
8304 }
8305 i++;
8306 }
8307 }
8308 if (!cmd->func) {
8309 ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
8310 return TRUE;
8311 }
8312 gboolean result = cmd->func(window, command, args);
8313 g_strfreev(args);
8314 return result;
8315 } else if (plugins_run_command(inp)) {
8316 return TRUE;
8317 } else {
8318 gboolean ran_alias = FALSE;
8319 gboolean alias_result = _cmd_execute_alias(window, inp, &ran_alias);
8320 if (!ran_alias) {
8321 return _cmd_execute_default(window, inp);
8322 } else {
8323 return alias_result;
8324 }
8325 }
8326 }
8327
8328 static gboolean
_cmd_execute_default(ProfWin * window,const char * inp)8329 _cmd_execute_default(ProfWin* window, const char* inp)
8330 {
8331 // handle escaped commands - treat as normal message
8332 if (g_str_has_prefix(inp, "//")) {
8333 inp++;
8334
8335 // handle unknown commands
8336 } else if ((inp[0] == '/') && (!g_str_has_prefix(inp, "/me "))) {
8337 cons_show("Unknown command: %s", inp);
8338 cons_alert(NULL);
8339 return TRUE;
8340 }
8341
8342 // handle non commands in non chat or plugin windows
8343 if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE && window->type != WIN_PLUGIN && window->type != WIN_XML) {
8344 cons_show("Unknown command: %s", inp);
8345 cons_alert(NULL);
8346 return TRUE;
8347 }
8348
8349 // handle plugin window
8350 if (window->type == WIN_PLUGIN) {
8351 ProfPluginWin* pluginwin = (ProfPluginWin*)window;
8352 plugins_win_process_line(pluginwin->tag, inp);
8353 return TRUE;
8354 }
8355
8356 jabber_conn_status_t status = connection_get_status();
8357 if (status != JABBER_CONNECTED) {
8358 win_println(window, THEME_DEFAULT, "-", "You are not currently connected.");
8359 return TRUE;
8360 }
8361
8362 switch (window->type) {
8363 case WIN_CHAT:
8364 {
8365 ProfChatWin* chatwin = (ProfChatWin*)window;
8366 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8367 cl_ev_send_msg(chatwin, inp, NULL);
8368 break;
8369 }
8370 case WIN_PRIVATE:
8371 {
8372 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
8373 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
8374 cl_ev_send_priv_msg(privatewin, inp, NULL);
8375 break;
8376 }
8377 case WIN_MUC:
8378 {
8379 ProfMucWin* mucwin = (ProfMucWin*)window;
8380 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8381 cl_ev_send_muc_msg(mucwin, inp, NULL);
8382 break;
8383 }
8384 case WIN_XML:
8385 {
8386 connection_send_stanza(inp);
8387 break;
8388 }
8389 default:
8390 break;
8391 }
8392
8393 return TRUE;
8394 }
8395
8396 static gboolean
_cmd_execute_alias(ProfWin * window,const char * const inp,gboolean * ran)8397 _cmd_execute_alias(ProfWin* window, const char* const inp, gboolean* ran)
8398 {
8399 if (inp[0] != '/') {
8400 *ran = FALSE;
8401 return TRUE;
8402 }
8403
8404 char* alias = strdup(inp + 1);
8405 char* value = prefs_get_alias(alias);
8406 free(alias);
8407 if (value) {
8408 *ran = TRUE;
8409 gboolean result = cmd_process_input(window, value);
8410 g_free(value);
8411 return result;
8412 }
8413
8414 *ran = FALSE;
8415 return TRUE;
8416 }
8417
8418 // helper function for status change commands
8419 static void
_update_presence(const resource_presence_t resource_presence,const char * const show,gchar ** args)8420 _update_presence(const resource_presence_t resource_presence,
8421 const char* const show, gchar** args)
8422 {
8423 char* msg = NULL;
8424 int num_args = g_strv_length(args);
8425
8426 // if no message, use status as message
8427 if (num_args == 2) {
8428 msg = args[1];
8429 } else {
8430 msg = args[2];
8431 }
8432
8433 jabber_conn_status_t conn_status = connection_get_status();
8434
8435 if (conn_status != JABBER_CONNECTED) {
8436 cons_show("You are not currently connected.");
8437 } else {
8438 connection_set_presence_msg(msg);
8439 cl_ev_presence_send(resource_presence, 0);
8440 ui_update_presence(resource_presence, msg, show);
8441 }
8442 }
8443
8444 // helper function for boolean preference commands
8445 static void
_cmd_set_boolean_preference(gchar * arg,const char * const command,const char * const display,preference_t pref)8446 _cmd_set_boolean_preference(gchar* arg, const char* const command,
8447 const char* const display, preference_t pref)
8448 {
8449 if (arg == NULL) {
8450 cons_bad_cmd_usage(command);
8451 } else if (strcmp(arg, "on") == 0) {
8452 GString* enabled = g_string_new(display);
8453 g_string_append(enabled, " enabled.");
8454
8455 cons_show(enabled->str);
8456 prefs_set_boolean(pref, TRUE);
8457
8458 g_string_free(enabled, TRUE);
8459 } else if (strcmp(arg, "off") == 0) {
8460 GString* disabled = g_string_new(display);
8461 g_string_append(disabled, " disabled.");
8462
8463 cons_show(disabled->str);
8464 prefs_set_boolean(pref, FALSE);
8465
8466 g_string_free(disabled, TRUE);
8467 } else {
8468 cons_bad_cmd_usage(command);
8469 }
8470 }
8471
8472 gboolean
cmd_omemo_gen(ProfWin * window,const char * const command,gchar ** args)8473 cmd_omemo_gen(ProfWin* window, const char* const command, gchar** args)
8474 {
8475 #ifdef HAVE_OMEMO
8476 if (connection_get_status() != JABBER_CONNECTED) {
8477 cons_show("You must be connected with an account to initialize OMEMO.");
8478 return TRUE;
8479 }
8480
8481 if (omemo_loaded()) {
8482 cons_show("OMEMO crytographic materials have already been generated.");
8483 return TRUE;
8484 }
8485
8486 cons_show("Generating OMEMO crytographic materials, it may take a while...");
8487 ui_update();
8488 ProfAccount* account = accounts_get_account(session_get_account_name());
8489 omemo_generate_crypto_materials(account);
8490 cons_show("OMEMO crytographic materials generated. Your Device ID is %d.", omemo_device_id());
8491 return TRUE;
8492 #else
8493 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8494 return TRUE;
8495 #endif
8496 }
8497
8498 gboolean
cmd_omemo_start(ProfWin * window,const char * const command,gchar ** args)8499 cmd_omemo_start(ProfWin* window, const char* const command, gchar** args)
8500 {
8501 #ifdef HAVE_OMEMO
8502 if (connection_get_status() != JABBER_CONNECTED) {
8503 cons_show("You must be connected with an account to load OMEMO information.");
8504 return TRUE;
8505 }
8506
8507 if (!omemo_loaded()) {
8508 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
8509 return TRUE;
8510 }
8511
8512 ProfChatWin* chatwin = NULL;
8513
8514 // recipient supplied
8515 if (args[1]) {
8516 char* contact = args[1];
8517 char* barejid = roster_barejid_from_name(contact);
8518 if (barejid == NULL) {
8519 barejid = contact;
8520 }
8521
8522 chatwin = wins_get_chat(barejid);
8523 if (!chatwin) {
8524 chatwin = chatwin_new(barejid);
8525 }
8526 ui_focus_win((ProfWin*)chatwin);
8527 } else {
8528 if (window->type == WIN_CHAT) {
8529 chatwin = (ProfChatWin*)window;
8530 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8531 }
8532 }
8533
8534 if (chatwin) {
8535 if (chatwin->pgp_send) {
8536 win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "You must disable PGP encryption before starting an OMEMO session.");
8537 return TRUE;
8538 }
8539
8540 if (chatwin->is_otr) {
8541 win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "You must disable OTR encryption before starting an OMEMO session.");
8542 return TRUE;
8543 }
8544
8545 if (chatwin->is_omemo) {
8546 win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "You are already in an OMEMO session.");
8547 return TRUE;
8548 }
8549
8550 accounts_add_omemo_state(session_get_account_name(), chatwin->barejid, TRUE);
8551 omemo_start_session(chatwin->barejid);
8552 chatwin->is_omemo = TRUE;
8553 } else if (window->type == WIN_MUC) {
8554 ProfMucWin* mucwin = (ProfMucWin*)window;
8555 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8556
8557 if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS
8558 && muc_member_type(mucwin->roomjid) == MUC_MEMBER_TYPE_MEMBERS_ONLY) {
8559 accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, TRUE);
8560 omemo_start_muc_sessions(mucwin->roomjid);
8561 mucwin->is_omemo = TRUE;
8562 } else {
8563 win_println(window, THEME_DEFAULT, "!", "MUC must be non-anonymous (i.e. be configured to present real jid to anyone) and members-only in order to support OMEMO.");
8564 }
8565 } else {
8566 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to start an OMEMO session.");
8567 }
8568
8569 return TRUE;
8570 #else
8571 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8572 return TRUE;
8573 #endif
8574 }
8575
8576 gboolean
cmd_omemo_trust_mode(ProfWin * window,const char * const command,gchar ** args)8577 cmd_omemo_trust_mode(ProfWin* window, const char* const command, gchar** args)
8578 {
8579 #ifdef HAVE_OMEMO
8580
8581 if (!args[1]) {
8582 cons_show("Current trust mode is %s", prefs_get_string(PREF_OMEMO_TRUST_MODE));
8583 return TRUE;
8584 }
8585
8586 if (g_strcmp0(args[1], "manual") == 0) {
8587 cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
8588 cons_show("You need to trust all OMEMO fingerprints manually");
8589 } else if (g_strcmp0(args[1], "firstusage") == 0) {
8590 cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
8591 cons_show("The first seen OMEMO fingerprints will be trusted automatically - new keys must be trusted manually");
8592 } else if (g_strcmp0(args[1], "blind") == 0) {
8593 cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
8594 cons_show("ALL OMEMO fingerprints will be trusted automatically");
8595 } else {
8596 cons_bad_cmd_usage(command);
8597 return TRUE;
8598 }
8599 prefs_set_string(PREF_OMEMO_TRUST_MODE, args[1]);
8600
8601 #else
8602 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8603 #endif
8604 return TRUE;
8605 }
8606
8607 gboolean
cmd_omemo_char(ProfWin * window,const char * const command,gchar ** args)8608 cmd_omemo_char(ProfWin* window, const char* const command, gchar** args)
8609 {
8610 #ifdef HAVE_OMEMO
8611 if (args[1] == NULL) {
8612 cons_bad_cmd_usage(command);
8613 return TRUE;
8614 } else if (g_utf8_strlen(args[1], 4) == 1) {
8615 if (prefs_set_omemo_char(args[1])) {
8616 cons_show("OMEMO char set to %s.", args[1]);
8617 } else {
8618 cons_show_error("Could not set OMEMO char: %s.", args[1]);
8619 }
8620 return TRUE;
8621 }
8622 cons_bad_cmd_usage(command);
8623 #else
8624 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8625 #endif
8626 return TRUE;
8627 }
8628
8629 gboolean
cmd_omemo_log(ProfWin * window,const char * const command,gchar ** args)8630 cmd_omemo_log(ProfWin* window, const char* const command, gchar** args)
8631 {
8632 #ifdef HAVE_OMEMO
8633 char* choice = args[1];
8634 if (g_strcmp0(choice, "on") == 0) {
8635 prefs_set_string(PREF_OMEMO_LOG, "on");
8636 cons_show("OMEMO messages will be logged as plaintext.");
8637 if (!prefs_get_boolean(PREF_CHLOG)) {
8638 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
8639 }
8640 } else if (g_strcmp0(choice, "off") == 0) {
8641 prefs_set_string(PREF_OMEMO_LOG, "off");
8642 cons_show("OMEMO message logging disabled.");
8643 } else if (g_strcmp0(choice, "redact") == 0) {
8644 prefs_set_string(PREF_OMEMO_LOG, "redact");
8645 cons_show("OMEMO messages will be logged as '[redacted]'.");
8646 if (!prefs_get_boolean(PREF_CHLOG)) {
8647 cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
8648 }
8649 } else {
8650 cons_bad_cmd_usage(command);
8651 }
8652 return TRUE;
8653 #else
8654 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8655 return TRUE;
8656 #endif
8657 }
8658
8659 gboolean
cmd_omemo_end(ProfWin * window,const char * const command,gchar ** args)8660 cmd_omemo_end(ProfWin* window, const char* const command, gchar** args)
8661 {
8662 #ifdef HAVE_OMEMO
8663 if (connection_get_status() != JABBER_CONNECTED) {
8664 cons_show("You must be connected with an account to load OMEMO information.");
8665 return TRUE;
8666 }
8667
8668 if (window->type == WIN_CHAT) {
8669 ProfChatWin* chatwin = (ProfChatWin*)window;
8670 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8671
8672 if (!chatwin->is_omemo) {
8673 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OMEMO session.");
8674 return TRUE;
8675 }
8676
8677 chatwin->is_omemo = FALSE;
8678 accounts_add_omemo_state(session_get_account_name(), chatwin->barejid, FALSE);
8679 } else if (window->type == WIN_MUC) {
8680 ProfMucWin* mucwin = (ProfMucWin*)window;
8681 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8682
8683 if (!mucwin->is_omemo) {
8684 win_println(window, THEME_DEFAULT, "!", "You are not currently in an OMEMO session.");
8685 return TRUE;
8686 }
8687
8688 mucwin->is_omemo = FALSE;
8689 accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, FALSE);
8690 } else {
8691 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to start an OMEMO session.");
8692 return TRUE;
8693 }
8694
8695 return TRUE;
8696 #else
8697 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8698 return TRUE;
8699 #endif
8700 }
8701
8702 gboolean
cmd_omemo_fingerprint(ProfWin * window,const char * const command,gchar ** args)8703 cmd_omemo_fingerprint(ProfWin* window, const char* const command, gchar** args)
8704 {
8705 #ifdef HAVE_OMEMO
8706 if (connection_get_status() != JABBER_CONNECTED) {
8707 cons_show("You must be connected with an account to load OMEMO information.");
8708 return TRUE;
8709 }
8710
8711 if (!omemo_loaded()) {
8712 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
8713 return TRUE;
8714 }
8715
8716 Jid* jid;
8717 if (!args[1]) {
8718 if (window->type == WIN_CONSOLE) {
8719 char* fingerprint = omemo_own_fingerprint(TRUE);
8720 cons_show("Your OMEMO fingerprint: %s", fingerprint);
8721 free(fingerprint);
8722 jid = jid_create(connection_get_fulljid());
8723 } else if (window->type == WIN_CHAT) {
8724 ProfChatWin* chatwin = (ProfChatWin*)window;
8725 jid = jid_create(chatwin->barejid);
8726 } else {
8727 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to print fingerprint without providing the contact.");
8728 return TRUE;
8729 }
8730 } else {
8731 char* barejid = roster_barejid_from_name(args[1]);
8732 if (barejid) {
8733 jid = jid_create(barejid);
8734 } else {
8735 jid = jid_create(args[1]);
8736 if (!jid) {
8737 cons_show("%s is not a valid jid", args[1]);
8738 return TRUE;
8739 }
8740 }
8741 }
8742
8743 GList* fingerprints = omemo_known_device_identities(jid->barejid);
8744
8745 if (!fingerprints) {
8746 win_println(window, THEME_DEFAULT, "-", "There is no known fingerprints for %s", jid->barejid);
8747 return TRUE;
8748 }
8749
8750 for (GList* fingerprint = fingerprints; fingerprint != NULL; fingerprint = fingerprint->next) {
8751 char* formatted_fingerprint = omemo_format_fingerprint(fingerprint->data);
8752 gboolean trusted = omemo_is_trusted_identity(jid->barejid, fingerprint->data);
8753
8754 win_println(window, THEME_DEFAULT, "-", "%s's OMEMO fingerprint: %s%s", jid->barejid, formatted_fingerprint, trusted ? " (trusted)" : "");
8755
8756 free(formatted_fingerprint);
8757 }
8758
8759 g_list_free(fingerprints);
8760
8761 win_println(window, THEME_DEFAULT, "-", "You can trust it with '/omemo trust <fingerprint>'");
8762 win_println(window, THEME_DEFAULT, "-", "You can untrust it with '/omemo untrust <fingerprint>'");
8763
8764 return TRUE;
8765 #else
8766 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8767 return TRUE;
8768 #endif
8769 }
8770
8771 gboolean
cmd_omemo_trust(ProfWin * window,const char * const command,gchar ** args)8772 cmd_omemo_trust(ProfWin* window, const char* const command, gchar** args)
8773 {
8774 #ifdef HAVE_OMEMO
8775 if (connection_get_status() != JABBER_CONNECTED) {
8776 cons_show("You must be connected with an account to load OMEMO information.");
8777 return TRUE;
8778 }
8779
8780 if (!args[1]) {
8781 cons_bad_cmd_usage(command);
8782 return TRUE;
8783 }
8784
8785 if (!omemo_loaded()) {
8786 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
8787 return TRUE;
8788 }
8789
8790 char* fingerprint;
8791 char* barejid;
8792
8793 /* Contact not provided */
8794 if (!args[2]) {
8795 fingerprint = args[1];
8796
8797 if (window->type != WIN_CHAT) {
8798 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to trust a device without providing the contact.");
8799 return TRUE;
8800 }
8801
8802 ProfChatWin* chatwin = (ProfChatWin*)window;
8803 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8804 barejid = chatwin->barejid;
8805 } else {
8806 fingerprint = args[2];
8807 char* contact = args[1];
8808 barejid = roster_barejid_from_name(contact);
8809 if (barejid == NULL) {
8810 barejid = contact;
8811 }
8812 }
8813
8814 omemo_trust(barejid, fingerprint);
8815
8816 char* unformatted_fingerprint = malloc(strlen(fingerprint));
8817 int i;
8818 int j;
8819 for (i = 0, j = 0; fingerprint[i] != '\0'; i++) {
8820 if (!g_ascii_isxdigit(fingerprint[i])) {
8821 continue;
8822 }
8823 unformatted_fingerprint[j++] = fingerprint[i];
8824 }
8825
8826 unformatted_fingerprint[j] = '\0';
8827 gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint);
8828
8829 win_println(window, THEME_DEFAULT, "-", "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : "");
8830
8831 free(unformatted_fingerprint);
8832
8833 return TRUE;
8834 #else
8835 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8836 return TRUE;
8837 #endif
8838 }
8839
8840 gboolean
cmd_omemo_untrust(ProfWin * window,const char * const command,gchar ** args)8841 cmd_omemo_untrust(ProfWin* window, const char* const command, gchar** args)
8842 {
8843 #ifdef HAVE_OMEMO
8844 if (connection_get_status() != JABBER_CONNECTED) {
8845 cons_show("You must be connected with an account to load OMEMO information.");
8846 return TRUE;
8847 }
8848
8849 if (!args[1]) {
8850 cons_bad_cmd_usage(command);
8851 return TRUE;
8852 }
8853
8854 if (!omemo_loaded()) {
8855 win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
8856 return TRUE;
8857 }
8858
8859 char* fingerprint;
8860 char* barejid;
8861
8862 /* Contact not provided */
8863 if (!args[2]) {
8864 fingerprint = args[1];
8865
8866 if (window->type != WIN_CHAT) {
8867 win_println(window, THEME_DEFAULT, "-", "You must be in a regular chat window to trust a device without providing the contact.");
8868 return TRUE;
8869 }
8870
8871 ProfChatWin* chatwin = (ProfChatWin*)window;
8872 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8873 barejid = chatwin->barejid;
8874 } else {
8875 fingerprint = args[2];
8876 char* contact = args[1];
8877 barejid = roster_barejid_from_name(contact);
8878 if (barejid == NULL) {
8879 barejid = contact;
8880 }
8881 }
8882
8883 omemo_untrust(barejid, fingerprint);
8884
8885 char* unformatted_fingerprint = malloc(strlen(fingerprint));
8886 int i, j;
8887 for (i = 0, j = 0; fingerprint[i] != '\0'; i++) {
8888 if (!g_ascii_isxdigit(fingerprint[i])) {
8889 continue;
8890 }
8891 unformatted_fingerprint[j++] = fingerprint[i];
8892 }
8893
8894 unformatted_fingerprint[j] = '\0';
8895 gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint);
8896
8897 win_println(window, THEME_DEFAULT, "-", "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : "");
8898
8899 free(unformatted_fingerprint);
8900
8901 return TRUE;
8902 #else
8903 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8904 return TRUE;
8905 #endif
8906 }
8907
8908 gboolean
cmd_omemo_clear_device_list(ProfWin * window,const char * const command,gchar ** args)8909 cmd_omemo_clear_device_list(ProfWin* window, const char* const command, gchar** args)
8910 {
8911 #ifdef HAVE_OMEMO
8912 if (connection_get_status() != JABBER_CONNECTED) {
8913 cons_show("You must be connected with an account to initialize OMEMO.");
8914 return TRUE;
8915 }
8916
8917 omemo_devicelist_publish(NULL);
8918 cons_show("Cleared OMEMO device list");
8919 return TRUE;
8920 #else
8921 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8922 return TRUE;
8923 #endif
8924 }
8925
8926 gboolean
cmd_omemo_policy(ProfWin * window,const char * const command,gchar ** args)8927 cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args)
8928 {
8929 #ifdef HAVE_OMEMO
8930 if (args[1] == NULL) {
8931 char* policy = prefs_get_string(PREF_OMEMO_POLICY);
8932 cons_show("OMEMO policy is now set to: %s", policy);
8933 g_free(policy);
8934 return TRUE;
8935 }
8936
8937 char* choice = args[1];
8938 if ((g_strcmp0(choice, "manual") != 0) && (g_strcmp0(choice, "automatic") != 0) && (g_strcmp0(choice, "always") != 0)) {
8939 cons_show("OMEMO policy can be set to: manual, automatic or always.");
8940 return TRUE;
8941 }
8942
8943 prefs_set_string(PREF_OMEMO_POLICY, choice);
8944 cons_show("OMEMO policy is now set to: %s", choice);
8945 return TRUE;
8946 #else
8947 cons_show("This version of Profanity has not been built with OMEMO support enabled");
8948 return TRUE;
8949 #endif
8950 }
8951
8952 gboolean
cmd_save(ProfWin * window,const char * const command,gchar ** args)8953 cmd_save(ProfWin* window, const char* const command, gchar** args)
8954 {
8955 log_info("Saving preferences to configuration file");
8956 cons_show("Saving preferences.");
8957 prefs_save();
8958 return TRUE;
8959 }
8960
8961 gboolean
cmd_reload(ProfWin * window,const char * const command,gchar ** args)8962 cmd_reload(ProfWin* window, const char* const command, gchar** args)
8963 {
8964 log_info("Reloading preferences");
8965 cons_show("Reloading preferences.");
8966 prefs_reload();
8967 return TRUE;
8968 }
8969
8970 gboolean
cmd_paste(ProfWin * window,const char * const command,gchar ** args)8971 cmd_paste(ProfWin* window, const char* const command, gchar** args)
8972 {
8973 #ifdef HAVE_GTK
8974 char* clipboard_buffer = clipboard_get();
8975
8976 if (clipboard_buffer) {
8977 switch (window->type) {
8978 case WIN_MUC:
8979 {
8980 ProfMucWin* mucwin = (ProfMucWin*)window;
8981 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
8982 cl_ev_send_muc_msg(mucwin, clipboard_buffer, NULL);
8983 break;
8984 }
8985 case WIN_CHAT:
8986 {
8987 ProfChatWin* chatwin = (ProfChatWin*)window;
8988 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
8989 cl_ev_send_msg(chatwin, clipboard_buffer, NULL);
8990 break;
8991 }
8992 case WIN_PRIVATE:
8993 {
8994 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
8995 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
8996 cl_ev_send_priv_msg(privatewin, clipboard_buffer, NULL);
8997 break;
8998 }
8999 case WIN_CONSOLE:
9000 case WIN_XML:
9001 default:
9002 cons_bad_cmd_usage(command);
9003 break;
9004 }
9005
9006 free(clipboard_buffer);
9007 }
9008 #else
9009 cons_show("This version of Profanity has not been built with GTK support enabled. It is needed for the clipboard feature to work.");
9010 #endif
9011
9012 return TRUE;
9013 }
9014
9015 gboolean
cmd_color(ProfWin * window,const char * const command,gchar ** args)9016 cmd_color(ProfWin* window, const char* const command, gchar** args)
9017 {
9018 if (g_strcmp0(args[0], "on") == 0) {
9019 prefs_set_string(PREF_COLOR_NICK, "true");
9020 } else if (g_strcmp0(args[0], "off") == 0) {
9021 prefs_set_string(PREF_COLOR_NICK, "false");
9022 } else if (g_strcmp0(args[0], "redgreen") == 0) {
9023 prefs_set_string(PREF_COLOR_NICK, "redgreen");
9024 } else if (g_strcmp0(args[0], "blue") == 0) {
9025 prefs_set_string(PREF_COLOR_NICK, "blue");
9026 } else if (g_strcmp0(args[0], "own") == 0) {
9027 if (g_strcmp0(args[1], "on") == 0) {
9028 _cmd_set_boolean_preference(args[1], command, "Color generation for own nick", PREF_COLOR_NICK_OWN);
9029 }
9030 } else {
9031 cons_bad_cmd_usage(command);
9032 return TRUE;
9033 }
9034
9035 cons_show("Consistent color generation for nicks set to: %s", args[0]);
9036
9037 char* theme = prefs_get_string(PREF_THEME);
9038 if (theme) {
9039 gboolean res = theme_load(theme, false);
9040
9041 if (res) {
9042 cons_show("Theme reloaded: %s", theme);
9043 } else {
9044 theme_load("default", false);
9045 }
9046
9047 g_free(theme);
9048 }
9049
9050 return TRUE;
9051 }
9052
9053 gboolean
cmd_avatar(ProfWin * window,const char * const command,gchar ** args)9054 cmd_avatar(ProfWin* window, const char* const command, gchar** args)
9055 {
9056 if (args[1] == NULL) {
9057 cons_bad_cmd_usage(command);
9058 return TRUE;
9059 }
9060
9061 if (g_strcmp0(args[0], "get") == 0) {
9062 avatar_get_by_nick(args[1], false);
9063 } else if (g_strcmp0(args[0], "open") == 0) {
9064 avatar_get_by_nick(args[1], true);
9065 } else if (g_strcmp0(args[0], "cmd") == 0) {
9066 prefs_set_string(PREF_AVATAR_CMD, args[1]);
9067 cons_show("Avatar cmd set to: %s", args[1]);
9068 }
9069
9070 return TRUE;
9071 }
9072
9073 gboolean
cmd_os(ProfWin * window,const char * const command,gchar ** args)9074 cmd_os(ProfWin* window, const char* const command, gchar** args)
9075 {
9076 _cmd_set_boolean_preference(args[0], command, "Revealing OS name", PREF_REVEAL_OS);
9077
9078 return TRUE;
9079 }
9080
9081 gboolean
cmd_correction(ProfWin * window,const char * const command,gchar ** args)9082 cmd_correction(ProfWin* window, const char* const command, gchar** args)
9083 {
9084 // enable/disable
9085 if (g_strcmp0(args[0], "on") == 0) {
9086 _cmd_set_boolean_preference(args[0], command, "Last Message Correction", PREF_CORRECTION_ALLOW);
9087 caps_add_feature(XMPP_FEATURE_LAST_MESSAGE_CORRECTION);
9088 return TRUE;
9089 } else if (g_strcmp0(args[0], "off") == 0) {
9090 _cmd_set_boolean_preference(args[0], command, "Last Message Correction", PREF_CORRECTION_ALLOW);
9091 caps_remove_feature(XMPP_FEATURE_LAST_MESSAGE_CORRECTION);
9092 return TRUE;
9093 }
9094
9095 // char
9096 if (g_strcmp0(args[0], "char") == 0) {
9097 if (args[1] == NULL) {
9098 cons_bad_cmd_usage(command);
9099 } else if (strlen(args[1]) != 1) {
9100 cons_bad_cmd_usage(command);
9101 } else {
9102 prefs_set_correction_char(args[1][0]);
9103 cons_show("LMC char set to %c.", args[1][0]);
9104 }
9105 }
9106
9107 return TRUE;
9108 }
9109
9110 gboolean
cmd_correct(ProfWin * window,const char * const command,gchar ** args)9111 cmd_correct(ProfWin* window, const char* const command, gchar** args)
9112 {
9113 jabber_conn_status_t conn_status = connection_get_status();
9114 if (conn_status != JABBER_CONNECTED) {
9115 cons_show("You are currently not connected.");
9116 return TRUE;
9117 }
9118
9119 if (!prefs_get_boolean(PREF_CORRECTION_ALLOW)) {
9120 win_println(window, THEME_DEFAULT, "!", "Corrections not enabled. See /help correction.");
9121 return TRUE;
9122 }
9123
9124 if (window->type == WIN_CHAT) {
9125 ProfChatWin* chatwin = (ProfChatWin*)window;
9126 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
9127
9128 if (chatwin->last_msg_id == NULL || chatwin->last_message == NULL) {
9129 win_println(window, THEME_DEFAULT, "!", "No last message to correct.");
9130 return TRUE;
9131 }
9132
9133 // send message again, with replace flag
9134 gchar* message = g_strjoinv(" ", args);
9135 cl_ev_send_msg_correct(chatwin, message, FALSE, TRUE);
9136
9137 free(message);
9138 return TRUE;
9139 } else if (window->type == WIN_MUC) {
9140 ProfMucWin* mucwin = (ProfMucWin*)window;
9141 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
9142
9143 if (mucwin->last_msg_id == NULL || mucwin->last_message == NULL) {
9144 win_println(window, THEME_DEFAULT, "!", "No last message to correct.");
9145 return TRUE;
9146 }
9147
9148 // send message again, with replace flag
9149 gchar* message = g_strjoinv(" ", args);
9150 cl_ev_send_muc_msg_corrected(mucwin, message, FALSE, TRUE);
9151
9152 free(message);
9153 return TRUE;
9154 }
9155
9156 win_println(window, THEME_DEFAULT, "!", "Command /correct only valid in regular chat windows.");
9157 return TRUE;
9158 }
9159
9160 gboolean
cmd_slashguard(ProfWin * window,const char * const command,gchar ** args)9161 cmd_slashguard(ProfWin* window, const char* const command, gchar** args)
9162 {
9163 if (args[0] == NULL) {
9164 return FALSE;
9165 }
9166
9167 _cmd_set_boolean_preference(args[0], command, "Slashguard", PREF_SLASH_GUARD);
9168
9169 return TRUE;
9170 }
9171
9172 #ifdef HAVE_OMEMO
9173 void
_url_aesgcm_method(ProfWin * window,const char * cmd_template,const char * url,const char * filename)9174 _url_aesgcm_method(ProfWin* window, const char* cmd_template, const char* url, const char* filename)
9175 {
9176 AESGCMDownload* download = malloc(sizeof(AESGCMDownload));
9177 download->window = window;
9178 download->url = strdup(url);
9179 download->filename = strdup(filename);
9180 if (cmd_template != NULL) {
9181 download->cmd_template = strdup(cmd_template);
9182 } else {
9183 download->cmd_template = NULL;
9184 }
9185
9186 pthread_create(&(download->worker), NULL, &aesgcm_file_get, download);
9187 aesgcm_download_add_download(download);
9188 }
9189 #endif
9190
9191 void
_url_http_method(ProfWin * window,const char * cmd_template,const char * url,const char * filename)9192 _url_http_method(ProfWin* window, const char* cmd_template, const char* url, const char* filename)
9193 {
9194
9195 HTTPDownload* download = malloc(sizeof(HTTPDownload));
9196 download->window = window;
9197 download->url = strdup(url);
9198 download->filename = strdup(filename);
9199 if (cmd_template != NULL) {
9200 download->cmd_template = strdup(cmd_template);
9201 } else {
9202 download->cmd_template = NULL;
9203 }
9204
9205 pthread_create(&(download->worker), NULL, &http_file_get, download);
9206 http_download_add_download(download);
9207 }
9208
9209 void
_url_external_method(const char * cmd_template,const char * url,const char * filename)9210 _url_external_method(const char* cmd_template, const char* url, const char* filename)
9211 {
9212 gchar** argv = format_call_external_argv(cmd_template, url, filename);
9213
9214 if (!call_external(argv, NULL, NULL)) {
9215 cons_show_error("Unable to call external executable for url: check the logs for more information.");
9216 } else {
9217 cons_show("URL '%s' has been called with '%s'.", url, cmd_template);
9218 }
9219
9220 g_strfreev(argv);
9221 }
9222
9223 gboolean
cmd_url_open(ProfWin * window,const char * const command,gchar ** args)9224 cmd_url_open(ProfWin* window, const char* const command, gchar** args)
9225 {
9226 if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
9227 cons_show_error("url open not supported in this window");
9228 return TRUE;
9229 }
9230
9231 gchar* url = args[1];
9232 if (url == NULL) {
9233 cons_bad_cmd_usage(command);
9234 return TRUE;
9235 }
9236
9237 gchar* scheme = NULL;
9238 char* cmd_template = NULL;
9239 char* filename = NULL;
9240
9241 scheme = g_uri_parse_scheme(url);
9242 if (scheme == NULL) {
9243 cons_show_error("URL '%s' is not valid.", args[1]);
9244 goto out;
9245 }
9246
9247 cmd_template = prefs_get_string(PREF_URL_OPEN_CMD);
9248 if (cmd_template == NULL) {
9249 cons_show_error("No default `url open` command found in executables preferences.");
9250 goto out;
9251 }
9252
9253 #ifdef HAVE_OMEMO
9254 // OMEMO URLs (aesgcm://) must be saved and decrypted before being opened.
9255 if (g_strcmp0(scheme, "aesgcm") == 0) {
9256
9257 // Ensure that the downloads directory exists for saving cleartexts.
9258 gchar* downloads_dir = files_get_data_path(DIR_DOWNLOADS);
9259 if (g_mkdir_with_parents(downloads_dir, S_IRWXU) != 0) {
9260 cons_show_error("Failed to create download directory "
9261 "at '%s' with error '%s'",
9262 downloads_dir, strerror(errno));
9263 g_free(downloads_dir);
9264 goto out;
9265 }
9266
9267 // Generate an unique filename from the URL that should be stored in the
9268 // downloads directory.
9269 filename = unique_filename_from_url(url, downloads_dir);
9270 g_free(downloads_dir);
9271
9272 // Download, decrypt and open the cleartext version of the AESGCM
9273 // encrypted file.
9274 _url_aesgcm_method(window, cmd_template, url, filename);
9275 goto out;
9276 }
9277 #endif
9278
9279 _url_external_method(cmd_template, url, NULL);
9280
9281 out:
9282
9283 free(cmd_template);
9284 free(filename);
9285
9286 g_free(scheme);
9287
9288 return TRUE;
9289 }
9290
9291 gboolean
cmd_url_save(ProfWin * window,const char * const command,gchar ** args)9292 cmd_url_save(ProfWin* window, const char* const command, gchar** args)
9293 {
9294 if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
9295 cons_show_error("`/url save` is not supported in this window.");
9296 return TRUE;
9297 }
9298
9299 if (args[1] == NULL) {
9300 cons_bad_cmd_usage(command);
9301 return TRUE;
9302 }
9303
9304 gchar* url = args[1];
9305 gchar* path = g_strdup(args[2]);
9306 gchar* scheme = NULL;
9307 char* filename = NULL;
9308 char* cmd_template = NULL;
9309
9310 scheme = g_uri_parse_scheme(url);
9311 if (scheme == NULL) {
9312 cons_show_error("URL '%s' is not valid.", args[1]);
9313 goto out;
9314 }
9315
9316 filename = unique_filename_from_url(url, path);
9317 if (filename == NULL) {
9318 cons_show_error("Failed to generate unique filename"
9319 "from URL '%s' for path '%s'",
9320 url, path);
9321 goto out;
9322 }
9323
9324 cmd_template = prefs_get_string(PREF_URL_SAVE_CMD);
9325 if (cmd_template == NULL && (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0)) {
9326 _url_http_method(window, cmd_template, url, filename);
9327 #ifdef HAVE_OMEMO
9328 } else if (g_strcmp0(scheme, "aesgcm") == 0) {
9329 _url_aesgcm_method(window, cmd_template, url, filename);
9330 #endif
9331 } else if (cmd_template != NULL) {
9332 _url_external_method(cmd_template, url, filename);
9333 } else {
9334 cons_show_error("No download method defined for the scheme '%s'.", scheme);
9335 }
9336
9337 out:
9338
9339 free(filename);
9340 free(cmd_template);
9341
9342 g_free(scheme);
9343 g_free(path);
9344
9345 return TRUE;
9346 }
9347
9348 gboolean
cmd_executable_avatar(ProfWin * window,const char * const command,gchar ** args)9349 cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args)
9350 {
9351 prefs_set_string(PREF_AVATAR_CMD, args[1]);
9352 cons_show("`avatar` command set to invoke '%s'", args[1]);
9353 return TRUE;
9354 }
9355
9356 gboolean
cmd_executable_urlopen(ProfWin * window,const char * const command,gchar ** args)9357 cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
9358 {
9359 guint num_args = g_strv_length(args);
9360 if (num_args < 2) {
9361 cons_bad_cmd_usage(command);
9362 return TRUE;
9363 }
9364
9365 if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
9366 gchar* str = g_strjoinv(" ", &args[2]);
9367 prefs_set_string(PREF_URL_OPEN_CMD, str);
9368 cons_show("`url open` command set to invoke '%s'", str);
9369 g_free(str);
9370
9371 } else if (g_strcmp0(args[1], "default") == 0) {
9372 prefs_set_string(PREF_URL_OPEN_CMD, NULL);
9373 gchar* def = prefs_get_string(PREF_URL_OPEN_CMD);
9374 cons_show("`url open` command set to invoke %s (default)", def);
9375 g_free(def);
9376 } else {
9377 cons_bad_cmd_usage(command);
9378 }
9379
9380 return TRUE;
9381 }
9382
9383 gboolean
cmd_executable_urlsave(ProfWin * window,const char * const command,gchar ** args)9384 cmd_executable_urlsave(ProfWin* window, const char* const command, gchar** args)
9385 {
9386
9387 guint num_args = g_strv_length(args);
9388 if (num_args < 2) {
9389 cons_bad_cmd_usage(command);
9390 return TRUE;
9391 }
9392
9393 if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
9394 gchar* str = g_strjoinv(" ", &args[2]);
9395 prefs_set_string(PREF_URL_SAVE_CMD, str);
9396 cons_show("`url save` command set to invoke '%s'", str);
9397 g_free(str);
9398
9399 } else if (g_strcmp0(args[1], "default") == 0) {
9400 prefs_set_string(PREF_URL_SAVE_CMD, NULL);
9401 cons_show("`url save` will use built-in download method (default)");
9402 } else {
9403 cons_bad_cmd_usage(command);
9404 }
9405
9406 return TRUE;
9407 }
9408
9409 gboolean
cmd_executable_editor(ProfWin * window,const char * const command,gchar ** args)9410 cmd_executable_editor(ProfWin* window, const char* const command, gchar** args)
9411 {
9412 guint num_args = g_strv_length(args);
9413
9414 if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
9415 prefs_set_string(PREF_COMPOSE_EDITOR, args[2]);
9416 cons_show("`editor` command set to invoke '%s'", args[2]);
9417 } else {
9418 cons_bad_cmd_usage(command);
9419 }
9420
9421 return TRUE;
9422 }
9423
9424 gboolean
cmd_mam(ProfWin * window,const char * const command,gchar ** args)9425 cmd_mam(ProfWin* window, const char* const command, gchar** args)
9426 {
9427 _cmd_set_boolean_preference(args[0], command, "Message Archive Management", PREF_MAM);
9428
9429 return TRUE;
9430 }
9431
9432 gboolean
cmd_change_password(ProfWin * window,const char * const command,gchar ** args)9433 cmd_change_password(ProfWin* window, const char* const command, gchar** args)
9434 {
9435 jabber_conn_status_t conn_status = connection_get_status();
9436
9437 if (conn_status != JABBER_CONNECTED) {
9438 cons_show("You are not currently connected.");
9439 return TRUE;
9440 }
9441
9442 char* user = connection_get_user();
9443 char* passwd = ui_ask_password(false);
9444 char* confirm_passwd = ui_ask_password(true);
9445
9446 if (g_strcmp0(passwd, confirm_passwd) == 0) {
9447 iq_register_change_password(user, passwd);
9448 } else {
9449 cons_show("Aborted! The new password and the confirmed password do not match.");
9450 }
9451
9452 free(user);
9453 free(passwd);
9454 free(confirm_passwd);
9455
9456 return TRUE;
9457 }
9458
9459 gboolean
cmd_editor(ProfWin * window,const char * const command,gchar ** args)9460 cmd_editor(ProfWin* window, const char* const command, gchar** args)
9461 {
9462 jabber_conn_status_t conn_status = connection_get_status();
9463
9464 if (conn_status != JABBER_CONNECTED) {
9465 cons_show("You are currently not connected.");
9466 return TRUE;
9467 }
9468
9469 // create editor dir if not present
9470 char *jid = connection_get_barejid();
9471 gchar *path = files_get_account_data_path(DIR_EDITOR, jid);
9472 if (g_mkdir_with_parents(path, S_IRWXU) != 0) {
9473 cons_show_error("Failed to create directory at '%s' with error '%s'", path, strerror(errno));
9474 free(jid);
9475 g_free(path);
9476 return TRUE;
9477 }
9478 // build temp file name. Example: /home/user/.local/share/profanity/editor/jid/compose.md
9479 char* filename = g_strdup_printf("%s/compose.md", path);
9480 free(jid);
9481 g_free(path);
9482
9483 GError* creation_error = NULL;
9484 GFile* file = g_file_new_for_path(filename);
9485 GFileOutputStream* fos = g_file_create(file, G_FILE_CREATE_PRIVATE, NULL, &creation_error);
9486
9487 free(filename);
9488
9489 if (creation_error) {
9490 cons_show_error("Editor: could not create temp file");
9491 return TRUE;
9492 }
9493 g_object_unref(fos);
9494
9495 char* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
9496
9497 // Fork / exec
9498 pid_t pid = fork();
9499 if (pid == 0) {
9500 int x = execlp(editor, editor, g_file_get_path(file), (char*)NULL);
9501 if (x == -1) {
9502 cons_show_error("Editor:Failed to exec %s", editor);
9503 }
9504 _exit(EXIT_FAILURE);
9505 } else {
9506 if (pid == -1) {
9507 return TRUE;
9508 }
9509 int status = 0;
9510 waitpid(pid, &status, 0);
9511 int fd_input_file = open(g_file_get_path(file), O_RDONLY);
9512 const size_t COUNT = 8192;
9513 char buf[COUNT];
9514 ssize_t size_read = read(fd_input_file, buf, COUNT);
9515 if (size_read > 0 && size_read <= COUNT) {
9516 buf[size_read - 1] = '\0';
9517 GString* text = g_string_new(buf);
9518 rl_insert_text(text->str);
9519 g_string_free(text, TRUE);
9520 }
9521 close(fd_input_file);
9522
9523 GError* deletion_error = NULL;
9524 g_file_delete(file, NULL, &deletion_error);
9525 if (deletion_error) {
9526 cons_show("Editor: error during file deletion");
9527 return TRUE;
9528 }
9529 g_object_unref(file);
9530 ui_resize();
9531 rl_point = rl_end;
9532 rl_forced_update_display();
9533 }
9534 return TRUE;
9535 }
9536
9537 gboolean
cmd_silence(ProfWin * window,const char * const command,gchar ** args)9538 cmd_silence(ProfWin* window, const char* const command, gchar** args)
9539 {
9540 _cmd_set_boolean_preference(args[0], command, "Block all messages from JIDs that are not in the roster", PREF_SILENCE_NON_ROSTER);
9541
9542 return TRUE;
9543 }
9544