1 /*
2  * cmd_defs.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  *
8  * This file is part of Profanity.
9  *
10  * Profanity is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Profanity is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
22  *
23  * In addition, as a special exception, the copyright holders give permission to
24  * link the code of portions of this program with the OpenSSL library under
25  * certain conditions as described in each individual source file, and
26  * distribute linked combinations including the two.
27  *
28  * You must obey the GNU General Public License in all respects for all of the
29  * code used other than OpenSSL. If you modify file(s) with this exception, you
30  * may extend this exception to your version of the file(s), but you are not
31  * obligated to do so. If you do not wish to do so, delete this exception
32  * statement from your version. If you delete this exception statement from all
33  * source files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <libgen.h>
46 #include <dirent.h>
47 #include <sys/types.h>
48 
49 #include <glib.h>
50 
51 #include "profanity.h"
52 #include "log.h"
53 #include "common.h"
54 #include "command/cmd_defs.h"
55 #include "command/cmd_funcs.h"
56 #include "command/cmd_ac.h"
57 #include "config/accounts.h"
58 #include "config/preferences.h"
59 #include "config/theme.h"
60 #include "config/tlscerts.h"
61 #include "config/scripts.h"
62 #include "plugins/plugins.h"
63 #include "tools/autocomplete.h"
64 #include "tools/parser.h"
65 #include "ui/ui.h"
66 #include "ui/window_list.h"
67 #include "xmpp/xmpp.h"
68 #include "xmpp/contact.h"
69 #include "xmpp/roster_list.h"
70 #include "xmpp/jid.h"
71 #include "xmpp/chat_session.h"
72 #include "xmpp/muc.h"
73 
74 #ifdef HAVE_LIBOTR
75 #include "otr/otr.h"
76 #endif
77 
78 #ifdef HAVE_LIBGPGME
79 #include "pgp/gpg.h"
80 #endif
81 
82 #define CMD_TAG_CHAT       "chat"
83 #define CMD_TAG_GROUPCHAT  "groupchat"
84 #define CMD_TAG_ROSTER     "roster"
85 #define CMD_TAG_PRESENCE   "presence"
86 #define CMD_TAG_CONNECTION "connection"
87 #define CMD_TAG_DISCOVERY  "discovery"
88 #define CMD_TAG_UI         "ui"
89 #define CMD_TAG_PLUGINS    "plugins"
90 
91 #define CMD_MAINFUNC(func) func,
92 #define CMD_NOMAINFUNC     NULL,
93 #define CMD_SUBFUNCS(...)  { __VA_ARGS__, { NULL, NULL } },
94 #define CMD_NOSUBFUNCS     { { NULL, NULL } },
95 
96 #define CMD_NOTAGS \
97     {              \
98         { NULL },
99 #define CMD_TAGS(...) \
100     {                 \
101         { __VA_ARGS__, NULL },
102 #define CMD_SYN(...)   { __VA_ARGS__, NULL },
103 #define CMD_DESC(desc) desc,
104 #define CMD_NOARGS     { { NULL, NULL } },
105 #define CMD_ARGS(...)  { __VA_ARGS__, { NULL, NULL } },
106 #define CMD_NOEXAMPLES \
107     {                  \
108         NULL           \
109     }                  \
110     }
111 #define CMD_EXAMPLES(...) \
112     {                     \
113         __VA_ARGS__, NULL \
114     }                     \
115     }
116 
117 GHashTable* commands = NULL;
118 
119 static gboolean _cmd_has_tag(Command* pcmd, const char* const tag);
120 
121 /*
122  * Command list
123  */
124 
125 // clang-format off
126 static struct cmd_t command_defs[] = {
127     { "/help",
128       parse_args_with_freetext, 0, 2, NULL,
129       CMD_NOSUBFUNCS
130       CMD_MAINFUNC(cmd_help)
131       CMD_NOTAGS
132       CMD_SYN(
133               "/help [<area>|<command>|search_all|search_any] [<search_terms>]")
134       CMD_DESC(
135               "Help on using Profanity. Passing no arguments list help areas. "
136               "For command help, optional arguments are shown using square brackets, "
137               "arguments representing variables rather than a literal name are surrounded by angle brackets. "
138               "Arguments that may be one of a number of values are separated by a pipe "
139               "e.g. val1|val2|val3.")
140       CMD_ARGS(
141               { "<area>", "Summary help for commands in a certain area of functionality." },
142               { "<command>", "Full help for a specific command, for example '/help connect'." },
143               { "search_all <search_terms>", "Search commands for returning matches that contain all of the search terms." },
144               { "search_any <search_terms>", "Search commands for returning matches that contain any of the search terms." })
145       CMD_EXAMPLES(
146               "/help search_all presence online",
147               "/help commands",
148               "/help presence",
149               "/help who")
150     },
151 
152     { "/about",
153       parse_args, 0, 0, NULL,
154       CMD_NOSUBFUNCS
155       CMD_MAINFUNC(cmd_about)
156       CMD_NOTAGS
157       CMD_SYN(
158               "/about")
159       CMD_DESC(
160               "Show version and license information.")
161       CMD_NOARGS
162       CMD_NOEXAMPLES
163     },
164 
165     { "/connect",
166       parse_args, 0, 7, NULL,
167       CMD_NOSUBFUNCS
168       CMD_MAINFUNC(cmd_connect)
169       CMD_TAGS(
170               CMD_TAG_CONNECTION)
171       CMD_SYN(
172               "/connect [<account>]",
173               "/connect <account> [server <server>] [port <port>] [tls force|allow|trust|legacy|disable] [auth default|legacy]")
174       CMD_DESC(
175               "Login to a chat service. "
176               "If no account is specified, the default is used if one is configured. "
177               "A local account is created with the JID as it's name if it doesn't already exist.")
178       CMD_ARGS(
179               { "<account>", "The local account you wish to connect with, or a JID if connecting for the first time." },
180               { "server <server>", "Supply a server if it is different to the domain part of your JID." },
181               { "port <port>", "The port to use if different to the default (5222, or 5223 for SSL)." },
182               { "tls force", "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
183               { "tls allow", "Use TLS for the connection if it is available." },
184               { "tls trust", "Force TLS connection and trust server's certificate." },
185               { "tls legacy", "Use legacy TLS for the connection. It means server doesn't support STARTTLS and TLS is forced just after TCP connection is established." },
186               { "tls disable", "Disable TLS for the connection." },
187               { "auth default", "Default authentication process." },
188               { "auth legacy", "Allow legacy authentication." })
189       CMD_EXAMPLES(
190               "/connect",
191               "/connect odin@valhalla.edda",
192               "/connect odin@valhalla.edda server talk.google.com",
193               "/connect freyr@vanaheimr.edda port 5678",
194               "/connect me@localhost.test.org server 127.0.0.1 tls disable",
195               "/connect me@chatty server chatty.com port 5443")
196     },
197 
198     { "/tls",
199       parse_args, 1, 3, NULL,
200       CMD_SUBFUNCS(
201               { "certpath", cmd_tls_certpath },
202               { "trust", cmd_tls_trust },
203               { "trusted", cmd_tls_trusted },
204               { "revoke", cmd_tls_revoke },
205               { "cert", cmd_tls_cert })
206       CMD_NOMAINFUNC
207           CMD_TAGS(
208                   CMD_TAG_CONNECTION,
209                   CMD_TAG_UI)
210           CMD_SYN(
211                   "/tls allow",
212                   "/tls always",
213                   "/tls deny",
214                   "/tls cert [<fingerprint>]",
215                   "/tls trust",
216                   "/tls trusted",
217                   "/tls revoke <fingerprint>",
218                   "/tls certpath",
219                   "/tls certpath set <path>",
220                   "/tls certpath clear",
221                   "/tls certpath default")
222           CMD_DESC(
223                   "Handle TLS certificates. ")
224           CMD_ARGS(
225                   { "allow", "Allow connection to continue with TLS certificate." },
226                   { "always", "Always allow connections with TLS certificate." },
227                   { "deny", "Abort connection." },
228                   { "cert", "Show the current TLS certificate." },
229                   { "cert <fingerprint>", "Show details of trusted certificate." },
230                   { "trust", "Add the current TLS certificate to manually trusted certificates." },
231                   { "trusted", "List summary of manually trusted certificates (with '/tls always' or '/tls trust')." },
232                   { "revoke <fingerprint>", "Remove a manually trusted certificate." },
233                   { "certpath", "Show the trusted certificate path." },
234                   { "certpath set <path>", "Specify filesystem path containing trusted certificates." },
235                   { "certpath clear", "Clear the trusted certificate path." },
236                   { "certpath default", "Use default system certificate path, if it can be found." })
237       CMD_NOEXAMPLES
238     },
239 
240     { "/disconnect",
241       parse_args, 0, 0, NULL,
242       CMD_NOSUBFUNCS
243       CMD_MAINFUNC(cmd_disconnect)
244       CMD_TAGS(
245               CMD_TAG_CONNECTION)
246       CMD_SYN(
247               "/disconnect")
248       CMD_DESC(
249               "Disconnect from the current chat service.")
250       CMD_NOARGS
251       CMD_NOEXAMPLES
252     },
253 
254     { "/msg",
255       parse_args_with_freetext, 1, 2, NULL,
256       CMD_NOSUBFUNCS
257       CMD_MAINFUNC(cmd_msg)
258       CMD_TAGS(
259               CMD_TAG_CHAT)
260       CMD_SYN(
261               "/msg <contact> [<message>]",
262               "/msg <nick> [<message>]")
263       CMD_DESC(
264               "Send a one to one chat message, or a private message to a chat room occupant. "
265               "If the message is omitted, a new chat window will be opened without sending a message. "
266               "Use quotes if the nickname includes spaces.")
267       CMD_ARGS(
268               { "<contact>", "Open chat window with contact, by JID or nickname." },
269               { "<contact> [<message>]", "Send message to contact, by JID or nickname." },
270               { "<nick>", "Open private chat window with chat room occupant." },
271               { "<nick> [<message>]", "Send a private message to a chat room occupant." })
272       CMD_EXAMPLES(
273               "/msg thor@valhalla.edda Hey, here's a message!",
274               "/msg heimdall@valhalla.edda",
275               "/msg Thor Here is a private message",
276               "/msg \"My Friend\" Hi, how are you?")
277     },
278 
279     { "/roster",
280       parse_args_with_freetext, 0, 4, NULL,
281       CMD_SUBFUNCS(
282               { "group", cmd_group })
283       CMD_MAINFUNC(cmd_roster)
284       CMD_TAGS(
285               CMD_TAG_ROSTER,
286               CMD_TAG_UI)
287       CMD_SYN(
288               "/roster",
289               "/roster online",
290               "/roster show [offline|resource|presence|status|empty|priority|contacts|rooms]",
291               "/roster hide [offline|resource|presence|status|empty|priority|contacts|rooms]",
292               "/roster by group|presence|none",
293               "/roster count unread|items|off",
294               "/roster count zero on|off",
295               "/roster color on|off",
296               "/roster order name|presence",
297               "/roster unread before|after|off",
298               "/roster room char <char>|none",
299               "/roster room private char <char>|none",
300               "/roster room position first|last",
301               "/roster room by service|none",
302               "/roster room order name|unread",
303               "/roster room unread before|after|off",
304               "/roster room show server",
305               "/roster room hide server",
306               "/roster room use name|jid",
307               "/roster private room|group|off",
308               "/roster private char <char>|none",
309               "/roster header char <char>|none",
310               "/roster presence indent <indent>",
311               "/roster contact char <char>|none",
312               "/roster contact indent <indent>",
313               "/roster resource char <char>|none",
314               "/roster resource indent <indent>",
315               "/roster resource join on|off",
316               "/roster size <percent>",
317               "/roster wrap on|off",
318               "/roster add <jid> [<nick>]",
319               "/roster remove <jid>",
320               "/roster remove_all contacts",
321               "/roster nick <jid> <nick>",
322               "/roster clearnick <jid>",
323               "/roster group",
324               "/roster group show <group>",
325               "/roster group add <group> <contat>",
326               "/roster group remove <group> <contact>")
327       CMD_DESC(
328               "Manage your roster, and roster display settings. "
329               "Passing no arguments lists all contacts in your roster.")
330       CMD_ARGS(
331               { "online", "Show all online contacts in console." },
332               { "show", "Show the roster panel." },
333               { "show offline", "Show offline contacts in roster panel." },
334               { "show resource", "Show contact's connected resources in roster panel." },
335               { "show presence", "Show contact's presence in roster panel." },
336               { "show status", "Show contact's status message in roster panel." },
337               { "show empty", "Show empty groups in roster panel." },
338               { "show priority", "Show resource priority in roster panel." },
339               { "show contacts", "Show contacts in roster panel." },
340               { "show rooms", "Show chat rooms in roster panel." },
341               { "hide", "Hide the roster panel." },
342               { "hide offline", "Hide offline contacts in roster panel." },
343               { "hide resource", "Hide contact's connected resources in roster panel." },
344               { "hide presence", "Hide contact's presence in roster panel." },
345               { "hide status", "Hide contact's status message in roster panel." },
346               { "hide empty", "Hide empty groups in roster panel." },
347               { "hide priority", "Hide resource priority in roster panel." },
348               { "hide contacts", "Hide contacts in roster panel." },
349               { "hide rooms", "Hide chat rooms in roster panel." },
350               { "by group", "Group contacts in roster panel by roster group." },
351               { "by presence", "Group contacts in roster panel by presence." },
352               { "by none", "No grouping in roster panel." },
353               { "count unread", "Show unread message count with roster headers." },
354               { "count items", "Show item count with roster headers." },
355               { "count off", "Do not show any count with roster headers." },
356               { "count zero on", "Show roster header count when 0." },
357               { "count zero off", "Hide roster header count when 0." },
358               { "color on", "Enable generated color names (XEP-0392)" },
359               { "color off", "Disable generated color names (XEP-0392)" },
360               { "order name", "Order roster contacts by name only." },
361               { "order presence", "Order roster contacts by presence, and then by name." },
362               { "unread before", "Show unread message count before contact." },
363               { "unread after", "Show unread message count after contact." },
364               { "unread off", "Do not show unread message count for contacts." },
365               { "room char <char>", "Prefix rooms with specified character." },
366               { "room char none", "Remove room character prefix." },
367               { "room private char <char>", "Prefix private room chat with specified character when displayed with room." },
368               { "room private char none", "Remove private room chat character prefix when displayed with room." },
369               { "room position first", "Show rooms first in roster." },
370               { "room position last", "Show rooms last in roster." },
371               { "room by service", "Group rooms by chat service." },
372               { "room by none", "Do not group rooms." },
373               { "room order name", "Order rooms by name." },
374               { "room order unread", "Order rooms by unread messages, and then by name." },
375               { "room unread before", "Show unread message count before room." },
376               { "room unread after", "Show unread message count after room." },
377               { "room unread off", "Do not show unread message count for rooms." },
378               { "room show server", "Show the conference server with room JIDs." },
379               { "room hide server", "Do not show the conference server with room JIDs." },
380               { "room use name", "Use the MUC name as room name." },
381               { "room use jid", "Use the JID as room name." },
382               { "private room", "Show room private chats with the room." },
383               { "private group", "Show room private chats as a separate roster group." },
384               { "private off", "Do not show room private chats." },
385               { "private char <char>", "Prefix private room chats with specified character when displayed in separate group." },
386               { "private char none", "Remove private room chat character prefix." },
387               { "header char <char>", "Prefix roster headers with specified character." },
388               { "header char none", "Remove roster header character prefix." },
389               { "contact char <char>", "Prefix roster contacts with specified character." },
390               { "contact char none", "Remove roster contact character prefix." },
391               { "contact indent <indent>", "Indent contact line by <indent> spaces (0 to 10)." },
392               { "resource char <char>", "Prefix roster resources with specified character." },
393               { "resource char none", "Remove roster resource character prefix." },
394               { "resource indent <indent>", "Indent resource line by <indent> spaces (0 to 10)." },
395               { "resource join on|off", "Join resource with previous line when only one available resource." },
396               { "presence indent <indent>", "Indent presence line by <indent> spaces (-1 to 10), a value of -1 will show presence on the previous line." },
397               { "size <percent>", "Percentage of the screen taken up by the roster (1-99)." },
398               { "wrap on|off", "Enable or disable line wrapping in roster panel." },
399               { "add <jid> [<nick>]", "Add a new item to the roster." },
400               { "remove <jid>", "Removes an item from the roster." },
401               { "remove_all contacts", "Remove all items from roster." },
402               { "nick <jid> <nick>", "Change a contacts nickname." },
403               { "clearnick <jid>", "Removes the current nickname." },
404               { "group show <group>", "List all roster items in a group." },
405               { "group add <group> <contact>", "Add a contact to a group." },
406               { "group remove <group> <contact>", "Remove a contact from a group." })
407       CMD_EXAMPLES(
408               "/roster",
409               "/roster add odin@valhalla.edda",
410               "/roster add odin@valhalla.edda Allfather",
411               "/roster remove loki@ownserver.org",
412               "/roster nick odin@valhalla.edda \"All Father\"",
413               "/roster clearnick thor@valhalla.edda",
414               "/roster size 15",
415               "/roster group",
416               "/roster group show friends",
417               "/roster group add friends fenris@ownserver.org",
418               "/roster group add family Brother",
419               "/roster group remove colleagues boss@work.com")
420     },
421 
422     { "/blocked",
423       parse_args_with_freetext, 0, 3, NULL,
424       CMD_NOSUBFUNCS
425       CMD_MAINFUNC(cmd_blocked)
426       CMD_TAGS(
427               CMD_TAG_ROSTER,
428               CMD_TAG_CHAT)
429       CMD_SYN(
430               "/blocked",
431               "/blocked add [<jid>]",
432               "/blocked report-abuse [<jid>] [<message>]",
433               "/blocked report-spam [<jid>] [<message>]",
434               "/blocked remove <jid>")
435       CMD_DESC(
436               "Manage blocked users (XEP-0191), calling with no arguments shows the current list of blocked users. "
437               "To blog a certain user in a MUC use the following as jid: room@conference.example.org/spammy-user"
438               "It is also possible to block and report (XEP-0377) a user with the report-abuse and report-spam commands.")
439       CMD_ARGS(
440               { "add [<jid>]", "Block the specified Jabber ID. If in a chat window and no jid is specified, the current recipient will be blocked." },
441               { "remove <jid>", "Remove the specified Jabber ID from the blocked list." },
442               { "report-abuse <jid> [<message>]", "Report the jid as abuse with an optional message to the service operator." },
443               { "report-spam <jid> [<message>]", "Report the jid as spam with an optional message to the service operator." })
444       CMD_EXAMPLES(
445               "/blocked add hel@helheim.edda",
446               "/blocked report-spam hel@helheim.edda Very annoying guy",
447               "/blocked add profanity@rooms.dismail.de/spammy-user")
448     },
449 
450     { "/info",
451       parse_args, 0, 1, NULL,
452       CMD_NOSUBFUNCS
453       CMD_MAINFUNC(cmd_info)
454       CMD_TAGS(
455               CMD_TAG_ROSTER,
456               CMD_TAG_CHAT,
457               CMD_TAG_GROUPCHAT)
458       CMD_SYN(
459               "/info",
460               "/info <contact>|<nick>")
461       CMD_DESC(
462               "Show information about a contact, room, or room member. "
463               "Passing no argument in a chat window will use the current recipient. "
464               "Passing no argument in a chat room will display information about the room.")
465       CMD_ARGS(
466               { "<contact>", "The contact you wish to view information about." },
467               { "<nick>", "When in a chat room, the occupant you wish to view information about." })
468       CMD_EXAMPLES(
469               "/info thor@aasgard.server.org",
470               "/info heimdall")
471     },
472 
473     { "/caps",
474       parse_args, 0, 1, NULL,
475       CMD_NOSUBFUNCS
476       CMD_MAINFUNC(cmd_caps)
477       CMD_TAGS(
478               CMD_TAG_DISCOVERY,
479               CMD_TAG_CHAT,
480               CMD_TAG_GROUPCHAT)
481       CMD_SYN(
482               "/caps",
483               "/caps <fulljid>|<nick>")
484       CMD_DESC(
485               "Find out a contacts, or room members client software capabilities. "
486               "If in private chat initiated from a chat room, no parameter is required.")
487       CMD_ARGS(
488               { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see capabilities." },
489               { "<nick>", "If in a chat room, nickname for which you wish to see capabilities." })
490       CMD_EXAMPLES(
491               "/caps ran@cold.sea.org/laptop",
492               "/caps ran@cold.sea.org/phone",
493               "/caps aegir")
494     },
495 
496     { "/software",
497       parse_args, 0, 1, NULL,
498       CMD_NOSUBFUNCS
499       CMD_MAINFUNC(cmd_software)
500       CMD_TAGS(
501               CMD_TAG_DISCOVERY,
502               CMD_TAG_CHAT,
503               CMD_TAG_GROUPCHAT)
504       CMD_SYN(
505               "/software",
506               "/software <fulljid>|<nick>")
507       CMD_DESC(
508               "Find out a contact, or room members software version information. "
509               "If in private chat initiated from a chat room, no parameter is required. "
510               "If the contact's software does not support software version requests, nothing will be displayed.")
511       CMD_ARGS(
512               { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see software information." },
513               { "<nick>", "If in a chat room, nickname for which you wish to see software information." })
514       CMD_EXAMPLES(
515               "/software odin@valhalla.edda/laptop",
516               "/software odin@valhalla.edda/phone",
517               "/software thor")
518     },
519 
520     { "/status",
521       parse_args, 2, 3, NULL,
522       CMD_SUBFUNCS(
523               { "get", cmd_status_get },
524               { "set", cmd_status_set })
525       CMD_NOMAINFUNC
526       CMD_TAGS(
527               CMD_TAG_CHAT,
528               CMD_TAG_GROUPCHAT)
529       CMD_SYN(
530               "/status set <state> [\"<message>\"]",
531               "/status get <contact>|<nick>")
532       CMD_DESC(
533               "/status get: Find out a contact, or room members presence information. "
534               "/status set: set own status.")
535       CMD_ARGS(
536               { "<state>", "Own status. Possible values: chat, online, away, dnd, xa" },
537               { "<message>", "Optional message to use with the status. Needs quotation marks if it's more than one word." },
538               { "<contact>", "The contact who's presence you which to see." },
539               { "<nick>", "If in a chat room, the occupant who's presence you wish to see." })
540       CMD_EXAMPLES(
541               "/status get odin@valhalla.edda",
542               "/status get jon",
543               "/status set online")
544     },
545 
546     { "/resource",
547       parse_args, 1, 2, &cons_resource_setting,
548       CMD_NOSUBFUNCS
549       CMD_MAINFUNC(cmd_resource)
550       CMD_TAGS(
551               CMD_TAG_CHAT,
552               CMD_TAG_UI)
553       CMD_SYN(
554               "/resource set <resource>",
555               "/resource off",
556               "/resource title on|off",
557               "/resource message on|off")
558       CMD_DESC(
559               "Override chat session resource, and manage resource display settings.")
560       CMD_ARGS(
561               { "set <resource>", "Set the resource to which messages will be sent." },
562               { "off", "Let the server choose which resource to route messages to." },
563               { "title on|off", "Show or hide the current resource in the titlebar." },
564               { "message on|off", "Show or hide the resource when showing an incoming message." })
565       CMD_NOEXAMPLES
566     },
567 
568     { "/join",
569       parse_args, 0, 5, NULL,
570       CMD_NOSUBFUNCS
571       CMD_MAINFUNC(cmd_join)
572       CMD_TAGS(
573               CMD_TAG_GROUPCHAT)
574       CMD_SYN(
575               "/join",
576               "/join <room> [nick <nick>] [password <password>]")
577       CMD_DESC(
578               "Join a chat room at the conference server. "
579               "If no room is supplied, a generated name will be used with the format private-chat-[UUID]. "
580               "If the domain part is not included in the room name, the account preference 'muc.service' will be used. "
581               "If no nickname is specified the account preference 'muc.nick' will be used which by default is the localpart of your JID. "
582               "If the room doesn't exist, and the server allows it, a new one will be created. "
583               "If you join to a room often, you might also want to add a bookmark (see `/help bookmark`), which also allows to set a default nickname. "
584               "In this case, you should use `/bookmark join`.")
585       CMD_ARGS(
586               { "<room>", "The chat room to join." },
587               { "nick <nick>", "Nickname to use in the room." },
588               { "password <password>", "Password if the room requires one." })
589       CMD_EXAMPLES(
590               "/join",
591               "/join profanity@rooms.dismail.de",
592               "/join profanity@rooms.dismail.de nick mynick",
593               "/join private@conference.jabber.org nick mynick password mypassword",
594               "/join mychannel")
595     },
596 
597     { "/invite",
598       parse_args_with_freetext, 1, 3, NULL,
599       CMD_NOSUBFUNCS
600       CMD_MAINFUNC(cmd_invite)
601       CMD_TAGS(
602               CMD_TAG_GROUPCHAT)
603       CMD_SYN(
604               "/invite send <contact> [<message>]",
605               "/invite list",
606               "/invite decline")
607       CMD_DESC(
608               "Manage room invites. "
609               "Send an invite to a contact for the current chat room. "
610               "List received invites. "
611               "Decline them using /invite decline and accept them using /join.")
612       CMD_ARGS(
613               { "send <contact> [<message>]", "The contact you wish to invite. And an optional message." },
614               { "list", "Show all rooms that you have been invited to, and not accepted or declined." },
615               { "decline <room>", "Decline a chat room invitation." })
616       CMD_EXAMPLES(
617               "/invite send gustavo@pollos.tx",
618               "/invite decline profanity@rooms.dismail.de",
619               "/invite list")
620     },
621 
622     { "/room",
623       parse_args, 1, 1, NULL,
624       CMD_NOSUBFUNCS
625       CMD_MAINFUNC(cmd_room)
626       CMD_TAGS(
627               CMD_TAG_GROUPCHAT)
628       CMD_SYN(
629               "/room accept|destroy|config")
630       CMD_DESC(
631               "Chat room configuration.")
632       CMD_ARGS(
633               { "accept", "Accept default room configuration." },
634               { "destroy", "Reject default room configuration, and destroy the room." },
635               { "config", "Edit room configuration." })
636       CMD_NOEXAMPLES
637     },
638 
639     { "/kick",
640       parse_args_with_freetext, 1, 2, NULL,
641       CMD_NOSUBFUNCS
642       CMD_MAINFUNC(cmd_kick)
643       CMD_TAGS(
644               CMD_TAG_GROUPCHAT)
645       CMD_SYN(
646               "/kick <nick> [<reason>]")
647       CMD_DESC(
648               "Kick occupant from chat room.")
649       CMD_ARGS(
650               { "<nick>", "Nickname of the occupant to kick from the room." },
651               { "<reason>", "Optional reason for kicking the occupant." })
652       CMD_NOEXAMPLES },
653 
654     { "/ban",
655       parse_args_with_freetext, 1, 2, NULL,
656       CMD_NOSUBFUNCS
657       CMD_MAINFUNC(cmd_ban)
658       CMD_TAGS(
659               CMD_TAG_GROUPCHAT)
660       CMD_SYN(
661               "/ban <jid> [<reason>]")
662       CMD_DESC(
663               "Ban user from chat room.")
664       CMD_ARGS(
665               { "<jid>", "Bare JID of the user to ban from the room." },
666               { "<reason>", "Optional reason for banning the user." })
667       CMD_NOEXAMPLES
668     },
669 
670     { "/subject",
671       parse_args_with_freetext, 0, 2, NULL,
672       CMD_NOSUBFUNCS
673       CMD_MAINFUNC(cmd_subject)
674       CMD_TAGS(
675               CMD_TAG_GROUPCHAT)
676       CMD_SYN(
677               "/subject set <subject>",
678               "/subject edit <subject>",
679               "/subject prepend <text>",
680               "/subject append <text>",
681               "/subject clear")
682       CMD_DESC(
683               "Set, modify, or clear room subject.")
684       CMD_ARGS(
685               { "set <subject>", "Set the room subject." },
686               { "edit <subject>", "Edit the current room subject, tab autocompletion will display the subject to edit." },
687               { "prepend <text>", "Prepend text to the current room subject, use double quotes if a trailing space is needed." },
688               { "append <text>", "Append text to the current room subject, use double quotes if a preceding space is needed." },
689               { "clear", "Clear the room subject." })
690       CMD_NOEXAMPLES
691     },
692 
693     { "/affiliation",
694       parse_args_with_freetext, 1, 4, NULL,
695       CMD_NOSUBFUNCS
696       CMD_MAINFUNC(cmd_affiliation)
697       CMD_TAGS(
698               CMD_TAG_GROUPCHAT)
699       CMD_SYN(
700               "/affiliation set <affiliation> <jid> [<reason>]",
701               "/affiliation list [<affiliation>]",
702               "/affiliation request",
703               "/affiliation register")
704       CMD_DESC(
705               "Manage room affiliations. "
706               "Affiliation may be one of owner, admin, member, outcast or none.")
707       CMD_ARGS(
708               { "set <affiliation> <jid> [<reason>]", "Set the affiliation of user with jid, with an optional reason." },
709               { "list [<affiliation>]", "List all users with the specified affiliation, or all if none specified." },
710               { "request", "Request voice."},
711               { "register", "Register your nickname with the MUC."})
712       CMD_NOEXAMPLES
713     },
714 
715     { "/role",
716       parse_args_with_freetext, 1, 4, NULL,
717       CMD_NOSUBFUNCS
718       CMD_MAINFUNC(cmd_role)
719       CMD_TAGS(
720               CMD_TAG_GROUPCHAT)
721       CMD_SYN(
722               "/role set <role> <nick> [<reason>]",
723               "/role list [<role>]")
724       CMD_DESC(
725               "Manage room roles. "
726               "Role may be one of moderator, participant, visitor or none.")
727       CMD_ARGS(
728               { "set <role> <nick> [<reason>]", "Set the role of occupant with nick, with an optional reason." },
729               { "list [<role>]", "List all occupants with the specified role, or all if none specified." })
730       CMD_NOEXAMPLES
731     },
732 
733     { "/occupants",
734       parse_args, 1, 3, cons_occupants_setting,
735       CMD_NOSUBFUNCS
736       CMD_MAINFUNC(cmd_occupants)
737       CMD_TAGS(
738               CMD_TAG_GROUPCHAT,
739               CMD_TAG_UI)
740       CMD_SYN(
741               "/occupants show|hide [jid|offline]",
742               "/occupants char <char>|none",
743               "/occupants color on|off",
744               "/occupants default show|hide [jid|offline]",
745               "/occupants size [<percent>]",
746               "/occupants indent <indent>",
747               "/occupants header char <char>|none",
748               "/occupants wrap on|off")
749       CMD_DESC(
750               "Show or hide room occupants, and occupants panel display settings.")
751       CMD_ARGS(
752               { "show", "Show the occupants panel in current room." },
753               { "char <char>", "Prefix occupants with specified character." },
754               { "char none", "Remove occupants character prefix." },
755               { "color on", "Enable generated color names (XEP-0392) for occupants" },
756               { "color off", "Disable generated color names (XEP-0392) for occupants" },
757               { "hide", "Hide the occupants panel in current room." },
758               { "show jid", "Show jid in the occupants panel in current room." },
759               { "hide jid", "Hide jid in the occupants panel in current room." },
760               { "show offline", "Show offline occupants panel in current room." },
761               { "hide offline", "Hide offline occupants panel in current room." },
762               { "default show|hide", "Whether occupants are shown by default in new rooms." },
763               { "default show|hide jid", "Whether occupants jids are shown by default in new rooms." },
764               { "default show|hide offline", "Whether offline occupants are shown by default in new rooms." },
765               { "size <percent>", "Percentage of the screen taken by the occupants list in rooms (1-99)." },
766               { "indent <indent>", "Indent contact line by <indent> spaces (0 to 10)." },
767               { "header char <char>", "Prefix occupants headers with specified character." },
768               { "header char none", "Remove occupants header character prefix." },
769               { "wrap on|off", "Enable or disable line wrapping in occupants panel." })
770       CMD_NOEXAMPLES
771     },
772 
773     { "/form",
774       parse_args, 1, 2, NULL,
775       CMD_NOSUBFUNCS
776       CMD_MAINFUNC(cmd_form)
777       CMD_TAGS(
778               CMD_TAG_GROUPCHAT)
779       CMD_SYN(
780               "/form show",
781               "/form submit",
782               "/form cancel",
783               "/form help [<tag>]")
784       CMD_DESC(
785               "Form configuration.")
786       CMD_ARGS(
787               { "show", "Show the current form." },
788               { "submit", "Submit the current form." },
789               { "cancel", "Cancel changes to the current form." },
790               { "help [<tag>]", "Display help for form, or a specific field." })
791       CMD_NOEXAMPLES
792     },
793 
794     { "/rooms",
795       parse_args, 0, 4, NULL,
796       CMD_NOSUBFUNCS
797       CMD_MAINFUNC(cmd_rooms)
798       CMD_TAGS(
799               CMD_TAG_GROUPCHAT)
800       CMD_SYN(
801               "/rooms",
802               "/rooms filter <text>",
803               "/rooms service <service>",
804               "/rooms service <service> filter <text>",
805               "/rooms cache on|off|clear")
806       CMD_DESC(
807               "List the chat rooms available at the specified conference service. "
808               "If no argument is supplied, the account preference 'muc.service' is used, 'conference.<domain-part>' by default. "
809               "The filter argument only shows rooms that contain the provided text, case insensitive.")
810       CMD_ARGS(
811               { "service <service>", "The conference service to query." },
812               { "filter <text>", "The text to filter results by." },
813               { "cache on|off", "Enable or disable caching of rooms list response, enabled by default." },
814               { "cache clear", "Clear the rooms response cache if enabled." })
815       CMD_EXAMPLES(
816               "/rooms",
817               "/rooms filter development",
818               "/rooms service conference.jabber.org",
819               "/rooms service conference.jabber.org filter \"News Room\"")
820     },
821 
822     { "/bookmark",
823       parse_args, 0, 8, NULL,
824       CMD_SUBFUNCS(
825               { "ignore", cmd_bookmark_ignore })
826       CMD_MAINFUNC(cmd_bookmark)
827       CMD_TAGS(
828               CMD_TAG_GROUPCHAT)
829       CMD_SYN(
830               "/bookmark",
831               "/bookmark list [<jid>]",
832               "/bookmark add [<room>] [nick <nick>] [password <password>] [name <roomname>] [autojoin on|off]",
833               "/bookmark update <room> [nick <nick>] [password <password>] [name <roomname>] [autojoin on|off]",
834               "/bookmark remove [<room>]",
835               "/bookmark join <room>",
836               "/bookmark invites on|off",
837               "/bookmark ignore",
838               "/bookmark ignore add <jid>",
839               "/bookmark ignore remove <jid>")
840       CMD_DESC(
841               "Manage bookmarks and join bookmarked rooms. "
842               "If you are in a chat room and no arguments are supplied to `/bookmark add`, autojoin is set to \"on\". "
843               "There is also an autojoin ignore list in case you want to autojoin in many clients but not on Profanity. ")
844       CMD_ARGS(
845               { "list [<jid>]", "List all bookmarks. Or the details of one." },
846               { "add [<room>]", "Add a bookmark, passing no room will bookmark the current room, setting autojoin to \"on\"." },
847               { "remove [<room>]", "Remove a bookmark, passing no room will remove the bookmark for the current room, if one exists." },
848               { "update <room>", "Update the properties associated with a bookmark." },
849               { "nick <nick>", "Nickname used when joining the chat room." },
850               { "password <password>", "Password if required, may be stored in plaintext on your server." },
851               { "name <roomname>", "Optional name for the bookmark. By default localpart of the JID will be used." },
852               { "autojoin on|off", "Whether to join the room automatically on login." },
853               { "join <room>", "Join room using the properties associated with the bookmark." },
854               { "invites on|off", "Whether or not to bookmark accepted room invites, defaults to 'on'." },
855               { "ignore add <barejid>", "Add a bookmark to the autojoin ignore list." },
856               { "ignore remove <barejid>", "Remove a bookmark from the autojoin ignore list." })
857       CMD_EXAMPLES(
858               "/bookmark add room@example.com nick YOURNICK",
859               "/bookmark join room@example.com",
860               "/bookmark update room@example.com nick NEWNICK autojoin on",
861               "/bookmark ignore room@example.com",
862               "/bookmark list",
863               "/bookmark list room@example.com",
864               "/bookmark remove room@example.com")
865     },
866 
867     { "/disco",
868       parse_args, 1, 2, NULL,
869       CMD_NOSUBFUNCS
870       CMD_MAINFUNC(cmd_disco)
871       CMD_TAGS(
872               CMD_TAG_DISCOVERY)
873       CMD_SYN(
874               "/disco info [<jid>]",
875               "/disco items [<jid>]")
876       CMD_DESC(
877               "Find out information about an entities supported services. "
878               "Calling with no arguments will query the server you are currently connected to. "
879               "This includes discovering contact addresses for XMPP services (XEP-0157).")
880       CMD_ARGS(
881               { "info [<jid>]", "List protocols and features supported by an entity." },
882               { "items [<jid>]", "List items associated with an entity." })
883       CMD_EXAMPLES(
884               "/disco info",
885               "/disco items myserver.org",
886               "/disco items conference.jabber.org",
887               "/disco info odin@valhalla.edda/laptop")
888     },
889 
890     { "/sendfile",
891       parse_args_with_freetext, 1, 1, NULL,
892       CMD_NOSUBFUNCS
893       CMD_MAINFUNC(cmd_sendfile)
894       CMD_TAGS(
895               CMD_TAG_CHAT,
896               CMD_TAG_GROUPCHAT)
897       CMD_SYN(
898               "/sendfile <file>")
899       CMD_DESC(
900               "Send a file using XEP-0363 HTTP file transfer.")
901       CMD_ARGS(
902               { "<file>", "Path to the file." })
903       CMD_EXAMPLES(
904               "/sendfile /etc/hosts",
905               "/sendfile ~/images/sweet_cat.jpg")
906     },
907 
908     { "/lastactivity",
909       parse_args, 1, 2, NULL,
910       CMD_NOSUBFUNCS
911       CMD_MAINFUNC(cmd_lastactivity)
912       CMD_TAGS(
913               CMD_TAG_PRESENCE)
914       CMD_SYN(
915               "/lastactivity set on|off",
916               "/lastactivity get [<jid>]")
917       CMD_DESC(
918               "Enable/disable sending last activity, and send last activity requests.")
919       CMD_ARGS(
920               { "on|off", "Enable or disable sending of last activity." },
921               { "<jid>", "The JID of the entity to query. Omitting the JID will query your server for its uptime." })
922       CMD_EXAMPLES(
923               "/lastactivity get",
924               "/lastactivity set off",
925               "/lastactivity get freyja@asgaard.edda",
926               "/lastactivity get freyja@asgaard.edda/laptop",
927               "/lastactivity get someserver.com")
928     },
929 
930     { "/nick",
931       parse_args_with_freetext, 1, 1, NULL,
932       CMD_NOSUBFUNCS
933       CMD_MAINFUNC(cmd_nick)
934       CMD_TAGS(
935               CMD_TAG_GROUPCHAT)
936       CMD_SYN(
937               "/nick <nickname>")
938       CMD_DESC(
939               "Change your nickname in the current chat room.")
940       CMD_ARGS(
941               { "<nickname>", "Your new nickname." })
942       CMD_NOEXAMPLES
943     },
944 
945     { "/win",
946       parse_args, 1, 1, NULL,
947       CMD_NOSUBFUNCS
948       CMD_MAINFUNC(cmd_win)
949       CMD_TAGS(
950               CMD_TAG_UI)
951       CMD_SYN(
952               "/win console",
953               "/win <num>",
954               "/win <barejid>",
955               "/win <nick>",
956               "/win <roomjid>",
957               "/win <roomoccupantjid>",
958               "/win xmlconsole",
959               "/win <plugin>")
960       CMD_DESC(
961               "Move to the specified window.")
962       CMD_ARGS(
963               { "console", "Focus the Console window." },
964               { "<num>", "Focus specified window number." },
965               { "<barejid>", "Focus chat window with contact by JID if open." },
966               { "<nick>", "Focus chat window with contact by nickname if open." },
967               { "<roomjid>", "Focus chat room window with roomjid if open." },
968               { "<roomoccupantjid>", "Focus private chat roomoccupantjid if open." },
969               { "xmlconsole", "Focus the XML Console window if open." },
970               { "<plugin>", "Focus the plugin window." })
971       CMD_EXAMPLES(
972               "/win console",
973               "/win 4",
974               "/win odin@valhalla.edda",
975               "/win Eddie",
976               "/win bigroom@conference.chat.org",
977               "/win bigroom@conference.chat.org/thor",
978               "/win wikipedia")
979     },
980 
981     { "/wins",
982       parse_args, 0, 3, NULL,
983       CMD_SUBFUNCS(
984               { "unread", cmd_wins_unread },
985               { "attention", cmd_wins_attention },
986               { "prune", cmd_wins_prune },
987               { "swap", cmd_wins_swap })
988       CMD_MAINFUNC(cmd_wins)
989       CMD_TAGS(
990               CMD_TAG_UI)
991       CMD_SYN(
992               "/wins",
993               "/wins unread",
994               "/wins attention",
995               "/wins prune",
996               "/wins swap <source> <target>")
997       CMD_DESC(
998               "Manage windows. "
999               "Passing no argument will list all currently active windows and information about their usage.")
1000       CMD_ARGS(
1001               { "unread", "List windows with unread messages." },
1002               { "attention", "List windows that have been marked with the attention flag (alt+v). You can toggle between marked windows with alt+m." },
1003               { "prune", "Close all windows with no unread messages." },
1004               { "swap <source> <target>", "Swap windows, target may be an empty position." })
1005       CMD_NOEXAMPLES
1006     },
1007 
1008     { "/sub",
1009       parse_args, 1, 2, NULL,
1010       CMD_NOSUBFUNCS
1011       CMD_MAINFUNC(cmd_sub)
1012       CMD_TAGS(
1013               CMD_TAG_ROSTER)
1014       CMD_SYN(
1015               "/sub request [<jid>]",
1016               "/sub allow [<jid>]",
1017               "/sub deny [<jid>]",
1018               "/sub show [<jid>]",
1019               "/sub sent",
1020               "/sub received")
1021       CMD_DESC(
1022               "Manage subscriptions to contact presence. "
1023               "If jid is omitted, the contact of the current window is used.")
1024       CMD_ARGS(
1025               { "request [<jid>]", "Send a subscription request to the user." },
1026               { "allow [<jid>]", "Approve a contact's subscription request." },
1027               { "deny [<jid>]", "Remove subscription for a contact, or deny a request." },
1028               { "show [<jid>]", "Show subscription status for a contact." },
1029               { "sent", "Show all sent subscription requests pending a response." },
1030               { "received", "Show all received subscription requests awaiting your response." })
1031       CMD_EXAMPLES(
1032               "/sub request odin@valhalla.edda",
1033               "/sub allow odin@valhalla.edda",
1034               "/sub request",
1035               "/sub sent")
1036     },
1037 
1038     { "/who",
1039       parse_args, 0, 2, NULL,
1040       CMD_NOSUBFUNCS
1041       CMD_MAINFUNC(cmd_who)
1042       CMD_TAGS(
1043               CMD_TAG_CHAT,
1044               CMD_TAG_GROUPCHAT,
1045               CMD_TAG_ROSTER)
1046       CMD_SYN(
1047               "/who",
1048               "/who online|offline|away|dnd|xa|chat|available|unavailable|any [<group>]",
1049               "/who moderator|participant|visitor",
1050               "/who owner|admin|member")
1051       CMD_DESC(
1052               "Show contacts or room occupants with chosen status, role or affiliation.")
1053       CMD_ARGS(
1054               { "offline|away|dnd|xa|chat", "Show contacts or room occupants with specified presence." },
1055               { "online", "Contacts that are online, chat, away, xa, dnd." },
1056               { "available", "Contacts that are available for chat - online, chat." },
1057               { "unavailable", "Contacts that are not available for chat - offline, away, xa, dnd." },
1058               { "any", "Contacts with any status (same as calling with no argument)." },
1059               { "<group>", "Filter the results by the specified roster group, not applicable in chat rooms." },
1060               { "moderator|participant|visitor", "Room occupants with the specified role." },
1061               { "owner|admin|member", "Room occupants with the specified affiliation." })
1062       CMD_EXAMPLES(
1063               "/who",
1064               "/who xa",
1065               "/who online friends",
1066               "/who any family",
1067               "/who participant",
1068               "/who admin")
1069     },
1070 
1071     { "/close",
1072       parse_args, 0, 1, NULL,
1073       CMD_NOSUBFUNCS
1074       CMD_MAINFUNC(cmd_close)
1075       CMD_TAGS(
1076               CMD_TAG_UI)
1077       CMD_SYN(
1078               "/close",
1079               "/close <num>",
1080               "/close <barejid>",
1081               "/close <nick>",
1082               "/close <roomjid>",
1083               "/close <roomoccupantjid>",
1084               "/close xmlconsole",
1085               "/close all|read")
1086       CMD_DESC(
1087               "Close windows. "
1088               "Passing no argument closes the current window.")
1089       CMD_ARGS(
1090               { "<num>", "Close specified window number." },
1091               { "<barejid>", "Close chat window with contact by JID if open." },
1092               { "<nick>", "Close chat window with contact by nickname if open." },
1093               { "<roomjid>", "Close chat room window with roomjid if open." },
1094               { "<roomoccupantjid>", "Close private chat roomoccupantjid if open." },
1095               { "xmlconsole", "Close the XML Console window if open." },
1096               { "all", "Close all windows." },
1097               { "read", "Close all windows that have no unread messages." })
1098       CMD_NOEXAMPLES
1099     },
1100 
1101     { "/clear",
1102       parse_args, 0, 2, NULL,
1103       CMD_NOSUBFUNCS
1104       CMD_MAINFUNC(cmd_clear)
1105       CMD_TAGS(
1106               CMD_TAG_UI)
1107       CMD_SYN(
1108               "/clear",
1109               "/clear persist_history <on|off>")
1110       CMD_DESC(
1111               "Clear the current window. "
1112               "If you set persist_history you can still access the history by pressing PAGE UP.")
1113       CMD_ARGS(
1114               { "persist_history on|off", "Whether or not to clear the screen persistently." })
1115       CMD_EXAMPLES(
1116               "/clear",
1117               "/clear persist_history",
1118               "/clear persist_history on")
1119     },
1120 
1121     { "/quit",
1122       parse_args, 0, 0, NULL,
1123       CMD_NOSUBFUNCS
1124        CMD_MAINFUNC(cmd_quit)
1125        CMD_NOTAGS
1126        CMD_SYN(
1127                "/quit")
1128        CMD_DESC(
1129                "Logout of any current session, and quit Profanity.")
1130        CMD_NOARGS
1131        CMD_NOEXAMPLES
1132     },
1133 
1134     { "/privileges",
1135       parse_args, 1, 1, &cons_privileges_setting,
1136       CMD_NOSUBFUNCS
1137       CMD_MAINFUNC(cmd_privileges)
1138       CMD_TAGS(
1139               CMD_TAG_GROUPCHAT,
1140               CMD_TAG_UI)
1141       CMD_SYN(
1142               "/privileges on|off")
1143       CMD_DESC(
1144               "Group occupants panel by role, and show role information in chat rooms.")
1145       CMD_ARGS(
1146               { "on|off", "Enable or disable privilege information." })
1147       CMD_NOEXAMPLES
1148     },
1149 
1150     { "/charset",
1151       parse_args, 0, 0, NULL,
1152       CMD_NOSUBFUNCS
1153       CMD_MAINFUNC(cmd_charset)
1154       CMD_TAGS(
1155               CMD_TAG_UI)
1156       CMD_SYN(
1157               "/charset")
1158       CMD_DESC(
1159               "Display information about the current character set supported by the terminal. ")
1160       CMD_NOARGS
1161       CMD_NOEXAMPLES
1162     },
1163 
1164     { "/beep",
1165       parse_args, 1, 1, &cons_beep_setting,
1166       CMD_NOSUBFUNCS
1167       CMD_MAINFUNC(cmd_beep)
1168       CMD_TAGS(
1169               CMD_TAG_UI)
1170       CMD_SYN(
1171               "/beep on|off")
1172       CMD_DESC(
1173               "Switch the terminal bell on or off. "
1174               "The bell will sound when incoming messages are received. "
1175               "If the terminal does not support sounds, it may attempt to flash the screen instead.")
1176       CMD_ARGS(
1177               { "on|off", "Enable or disable terminal bell." })
1178       CMD_NOEXAMPLES
1179     },
1180 
1181     { "/console",
1182       parse_args, 2, 2, &cons_console_setting,
1183       CMD_NOSUBFUNCS
1184       CMD_MAINFUNC(cmd_console)
1185       CMD_TAGS(
1186               CMD_TAG_UI,
1187               CMD_TAG_CHAT,
1188               CMD_TAG_GROUPCHAT)
1189       CMD_SYN(
1190               "/console chat all|first|none",
1191               "/console muc all|first|mention|none",
1192               "/console private all|first|none")
1193       CMD_DESC(
1194               "Configure what is displayed in the console window when messages are received. "
1195               "The default is set to 'all' for all types of messages.")
1196       CMD_ARGS(
1197               { "chat all", "Indicate all new chat messages in the console." },
1198               { "chat first", "Indicate only the first new message per chat in the console." },
1199               { "chat none", "Do not show any new chat messages in the console window." },
1200               { "muc all", "Indicate all new chat room messages in the console." },
1201               { "muc first", "Indicate only the first new message in each room in the console." },
1202               { "muc mention", "Indicate only messages in which you have beeen mentioned in the console." },
1203               { "muc none", "Do not show any new chat room messages in the console window." },
1204               { "private all", "Indicate all new private room messages in the console." },
1205               { "private first", "Indicate only the first private room message in the console." },
1206               { "private none", "Do not show any new private room messages in the console window." })
1207       CMD_NOEXAMPLES
1208     },
1209 
1210     { "/presence",
1211       parse_args, 2, 2, &cons_presence_setting,
1212       CMD_NOSUBFUNCS
1213       CMD_MAINFUNC(cmd_presence)
1214       CMD_TAGS(
1215               CMD_TAG_UI,
1216               CMD_TAG_CHAT,
1217               CMD_TAG_GROUPCHAT)
1218       CMD_SYN(
1219               "/presence titlebar on|off",
1220               "/presence console all|online|none",
1221               "/presence chat all|online|none",
1222               "/presence room all|online|none")
1223       CMD_DESC(
1224               "Show the contacts presence in the titlebar and configure presence messages in different window types.")
1225       CMD_ARGS(
1226               { "titlebar on|off", "Switch display of the contacts presence in the titlebar on or off." },
1227               { "console all", "Show all presence changes in the console window." },
1228               { "console online", "Show only online/offline presence changes in the console window." },
1229               { "console none", "Don't show any presence changes in the console window." },
1230               { "chat all", "Show all presence changes in the chat windows." },
1231               { "chat online", "Show only online/offline presence changes in chat windows." },
1232               { "chat none", "Don't show any presence changes in chat windows." },
1233               { "room all", "Show all presence changes in chat room windows." },
1234               { "room online", "Show only online/offline presence changes in chat room windows." },
1235               { "room none", "Don't show any presence changes in chat room windows." })
1236       CMD_EXAMPLES(
1237               "/presence titlebar off",
1238               "/presence console none",
1239               "/presence chat online",
1240               "/presence room all")
1241     },
1242 
1243     { "/wrap",
1244       parse_args, 1, 1, &cons_wrap_setting,
1245       CMD_NOSUBFUNCS
1246       CMD_MAINFUNC(cmd_wrap)
1247       CMD_TAGS(
1248               CMD_TAG_UI)
1249       CMD_SYN(
1250               "/wrap on|off")
1251       CMD_DESC(
1252               "Word wrapping.")
1253       CMD_ARGS(
1254               { "on|off", "Enable or disable word wrapping in the main window." })
1255       CMD_NOEXAMPLES
1256     },
1257 
1258     { "/time",
1259       parse_args, 1, 3, &cons_time_setting,
1260       CMD_NOSUBFUNCS
1261       CMD_MAINFUNC(cmd_time)
1262       CMD_TAGS(
1263               CMD_TAG_UI)
1264       CMD_SYN(
1265               "/time all|console|chat|muc|config|private|xml set <format>",
1266               "/time all|console|chat|muc|config|private|xml off",
1267               "/time statusbar set <format>",
1268               "/time statusbar off",
1269               "/time lastactivity set <format>")
1270       CMD_DESC(
1271               "Configure time display preferences. "
1272               "Time formats are strings supported by g_date_time_format. "
1273               "See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format for more details. "
1274               "Setting the format to an unsupported string, will display the string. "
1275               "If the format contains spaces, it must be surrounded with double quotes.")
1276       CMD_ARGS(
1277               { "console set <format>", "Set time format for console window." },
1278               { "console off", "Do not show time in console window." },
1279               { "chat set <format>", "Set time format for chat windows." },
1280               { "chat off", "Do not show time in chat windows." },
1281               { "muc set <format>", "Set time format for chat room windows." },
1282               { "muc off", "Do not show time in chat room windows." },
1283               { "config set <format>", "Set time format for config windows." },
1284               { "config off", "Do not show time in config windows." },
1285               { "private set <format>", "Set time format for private chat windows." },
1286               { "private off", "Do not show time in private chat windows." },
1287               { "xml set <format>", "Set time format for XML console window." },
1288               { "xml off", "Do not show time in XML console window." },
1289               { "statusbar set <format>", "Change time format in statusbar." },
1290               { "statusbar off", "Do not show time in status bar." },
1291               { "lastactivity set <format>", "Change time format for last activity." },
1292               { "all set <format>", "Set time for: console, chat, muc, config, private and xml windows." },
1293               { "all off", "Do not show time for: console, chat, muc, config, private and xml windows." })
1294       CMD_EXAMPLES(
1295               "/time console set %H:%M:%S",
1296               "/time chat set \"%d-%m-%y %H:%M:%S\"",
1297               "/time xml off",
1298               "/time statusbar set %H:%M",
1299               "/time lastactivity set \"%d-%m-%y %H:%M:%S\"",
1300               "/time all set \"%d-%m-%y %H:%M:%S\"")
1301     },
1302 
1303     { "/inpblock",
1304       parse_args, 2, 2, &cons_inpblock_setting,
1305       CMD_NOSUBFUNCS
1306       CMD_MAINFUNC(cmd_inpblock)
1307       CMD_TAGS(
1308               CMD_TAG_UI)
1309       CMD_SYN(
1310               "/inpblock timeout <millis>",
1311               "/inpblock dynamic on|off")
1312       CMD_DESC(
1313               "How long to wait for keyboard input before checking for new messages or checking for state changes such as 'idle'.")
1314       CMD_ARGS(
1315               { "timeout <millis>", "Time to wait (1-1000) in milliseconds before reading input from the terminal buffer, default: 1000." },
1316               { "dynamic on|off", "Start with 0 millis and dynamically increase up to timeout when no activity, default: on." })
1317       CMD_NOEXAMPLES },
1318 
1319 
1320     { "/titlebar",
1321       parse_args, 1, 2, &cons_titlebar_setting,
1322       CMD_SUBFUNCS(
1323               { "show", cmd_titlebar_show_hide },
1324               { "hide", cmd_titlebar_show_hide })
1325       CMD_MAINFUNC(cmd_titlebar)
1326       CMD_TAGS(
1327               CMD_TAG_UI)
1328       CMD_SYN(
1329               "/titlebar up",
1330               "/titlebar down",
1331               "/titlebar show|hide [encwarn|resource|tls]")
1332       CMD_DESC(
1333               "Titlebar settings.")
1334       CMD_ARGS(
1335               { "up", "Move the title bar up the screen." },
1336               { "down", "Move the title bar down the screen." },
1337               { "show tls", "Show or hide TLS indicator in the titlebar." },
1338               { "show encwarn", "Enable or disable the unencrypted warning message in the titlebar." },
1339               { "show resource", "Show or hide the current resource in the titlebar." },
1340               { "show name", "In case of a MUC. Show the MUC name in the titlebar." },
1341               { "show jid", "In case of a MUC. Show the JID in the titlebar." })
1342       CMD_EXAMPLES(
1343               "/titlebar up",
1344               "/titlebar show tls",
1345               "/titlebar hide encwarn")
1346     },
1347 
1348     { "/mainwin",
1349       parse_args, 1, 1, &cons_winpos_setting,
1350       CMD_NOSUBFUNCS
1351       CMD_MAINFUNC(cmd_mainwin)
1352       CMD_TAGS(
1353               CMD_TAG_UI)
1354       CMD_SYN(
1355               "/mainwin up",
1356               "/mainwin down")
1357       CMD_DESC(
1358               "Move the main window.")
1359       CMD_ARGS(
1360               { "up", "Move the main window up the screen." },
1361               { "down", "Move the main window down the screen." })
1362       CMD_NOEXAMPLES
1363     },
1364 
1365     { "/statusbar",
1366       parse_args, 1, 2, &cons_statusbar_setting,
1367       CMD_NOSUBFUNCS
1368       CMD_MAINFUNC(cmd_statusbar)
1369       CMD_TAGS(
1370               CMD_TAG_UI)
1371       CMD_SYN(
1372               "/statusbar show name|number|read",
1373               "/statusbar hide name|number|read",
1374               "/statusbar maxtabs <value>",
1375               "/statusbar tablen <value>",
1376               "/statusbar self user|barejid|fulljid|off",
1377               "/statusbar chat user|jid",
1378               "/statusbar room room|jid",
1379               "/statusbar up",
1380               "/statusbar down")
1381       CMD_DESC(
1382               "Manage statusbar display preferences.")
1383       CMD_ARGS(
1384               { "maxtabs <value>", "Set the maximum number of tabs to display, <value> must be between 0 and 10." },
1385               { "tablen <value>", "Set the maximum number of characters to show as the tab name, 0 sets to unlimited." },
1386               { "show|hide name", "Show or hide names in tabs." },
1387               { "show|hide number", "Show or hide numbers in tabs." },
1388               { "show|hide read", "Show or hide inactive tabs." },
1389               { "self user|barejid|fulljid", "Show account user name, barejid, fulljid as status bar title." },
1390               { "self off", "Disable showing self as status bar title." },
1391               { "chat user|jid", "Show users name, or the fulljid if no nick is present for chat tabs." },
1392               { "room room|jid", "Show room name, or the fulljid for room tabs." },
1393               { "up", "Move the status bar up the screen." },
1394               { "down", "Move the status bar down the screen." })
1395       CMD_EXAMPLES(
1396               "/statusbar maxtabs 8",
1397               "/statusbar tablen 5",
1398               "/statusbar self user",
1399               "/statusbar chat jid",
1400               "/statusbar hide read",
1401               "/statusbar hide name")
1402     },
1403 
1404     { "/inputwin",
1405       parse_args, 1, 1, &cons_winpos_setting,
1406       CMD_NOSUBFUNCS
1407       CMD_MAINFUNC(cmd_inputwin)
1408       CMD_TAGS(
1409               CMD_TAG_UI)
1410       CMD_SYN(
1411               "/inputwin up",
1412               "/inputwin down")
1413       CMD_DESC(
1414               "Move the input window.")
1415       CMD_ARGS(
1416               { "up", "Move the input window up the screen." },
1417               { "down", "Move the input window down the screen." })
1418       CMD_NOEXAMPLES
1419     },
1420 
1421     { "/notify",
1422       parse_args_with_freetext, 0, 4, NULL,
1423       CMD_NOSUBFUNCS
1424       CMD_MAINFUNC(cmd_notify)
1425       CMD_TAGS(
1426               CMD_TAG_UI,
1427               CMD_TAG_CHAT,
1428               CMD_TAG_GROUPCHAT)
1429       CMD_SYN(
1430               "/notify chat on|off",
1431               "/notify chat current on|off",
1432               "/notify chat text on|off",
1433               "/notify room on|off",
1434               "/notify room mention on|off",
1435               "/notify room mention case_sensitive|case_insensitive",
1436               "/notify room mention word_whole|word_part",
1437               "/notify room current on|off",
1438               "/notify room text on|off",
1439               "/notify room trigger add <text>",
1440               "/notify room trigger remove <text>",
1441               "/notify room trigger list",
1442               "/notify room trigger on|off",
1443               "/notify on|off",
1444               "/notify mention on|off",
1445               "/notify trigger on|off",
1446               "/notify reset",
1447               "/notify remind <seconds>",
1448               "/notify typing on|off",
1449               "/notify typing current on|off",
1450               "/notify invite on|off",
1451               "/notify sub on|off")
1452       CMD_DESC(
1453               "Configure desktop notifications. "
1454               "To configure presence update messages in the console, chat and chat room windows, see '/help presence'.")
1455       CMD_ARGS(
1456               { "chat on|off", "Notifications for regular chat messages." },
1457               { "chat current on|off", "Whether to show regular chat message notifications when the window is focused." },
1458               { "chat text on|off", "Show message text in regular message notifications." },
1459               { "room on|off", "Notifications for all chat room messages." },
1460               { "room mention on|off", "Notifications for chat room messages when your nick is mentioned." },
1461               { "room mention case_sensitive", "Set room mention notifications as case sensitive." },
1462               { "room mention case_insensitive", "Set room mention notifications as case insensitive." },
1463               { "room mention word_whole", "Set room mention notifications only on whole word match, i.e. when nickname is not part of a larger word." },
1464               { "room mention word_part", "Set room mention notifications on partial word match, i.e. nickname may be part of a larger word." },
1465               { "room current on|off", "Whether to show all chat room messages notifications when the window is focused." },
1466               { "room text on|off", "Show message text in chat room message notifications." },
1467               { "room trigger add <text>", "Notify when specified text included in all chat room messages." },
1468               { "room trigger remove <text>", "Remove chat room notification trigger." },
1469               { "room trigger list", "List all chat room highlight triggers." },
1470               { "room trigger on|off", "Enable or disable all chat room notification triggers." },
1471               { "on|off", "Override the global message setting for the current chat room." },
1472               { "mention on|off", "Override the global 'mention' setting for the current chat room." },
1473               { "trigger on|off", "Override the global 'trigger' setting for the current chat room." },
1474               { "reset", "Reset to global notification settings for the current chat room." },
1475               { "remind <seconds>", "Notification reminder period for unread messages, use 0 to disable." },
1476               { "typing on|off", "Notifications when contacts are typing." },
1477               { "typing current on|off", "Whether typing notifications are triggered for the current window." },
1478               { "invite on|off", "Notifications for chat room invites." },
1479               { "sub on|off", "Notifications for subscription requests." })
1480       CMD_EXAMPLES(
1481               "/notify chat on",
1482               "/notify chat text on",
1483               "/notify room mention on",
1484               "/notify room trigger add beer",
1485               "/notify room trigger on",
1486               "/notify room current off",
1487               "/notify room text off",
1488               "/notify remind 60",
1489               "/notify typing on",
1490               "/notify invite on")
1491     },
1492 
1493     { "/flash",
1494       parse_args, 1, 1, &cons_flash_setting,
1495       CMD_NOSUBFUNCS
1496       CMD_MAINFUNC(cmd_flash)
1497       CMD_TAGS(
1498               CMD_TAG_UI)
1499       CMD_SYN(
1500               "/flash on|off")
1501       CMD_DESC(
1502               "Make the terminal flash when incoming messages are received in another window. "
1503               "If the terminal doesn't support flashing, it may attempt to beep.")
1504       CMD_ARGS(
1505               { "on|off", "Enable or disable terminal flash." })
1506       CMD_NOEXAMPLES
1507     },
1508 
1509     { "/tray",
1510       parse_args, 1, 2, &cons_tray_setting,
1511       CMD_NOSUBFUNCS
1512       CMD_MAINFUNC(cmd_tray)
1513       CMD_TAGS(
1514               CMD_TAG_UI)
1515       CMD_SYN(
1516               "/tray on|off",
1517               "/tray read on|off",
1518               "/tray timer <seconds>")
1519       CMD_DESC(
1520               "Display an icon in the tray that will indicate new messages.")
1521       CMD_ARGS(
1522               { "on|off", "Show tray icon." },
1523               { "read on|off", "Show tray icon when no unread messages." },
1524               { "timer <seconds>", "Set tray icon timer, seconds must be between 1-10." })
1525       CMD_NOEXAMPLES
1526     },
1527 
1528     { "/intype",
1529       parse_args, 2, 2, &cons_intype_setting,
1530       CMD_NOSUBFUNCS
1531       CMD_MAINFUNC(cmd_intype)
1532       CMD_TAGS(
1533               CMD_TAG_UI,
1534               CMD_TAG_CHAT)
1535       CMD_SYN(
1536               "/intype console|titlebar on|off")
1537       CMD_DESC(
1538               "Show when a contact is typing in the console, and in active message window.")
1539       CMD_ARGS(
1540               { "titlebar on|off", "Enable or disable contact typing messages notification in titlebar." },
1541               { "console on|off", "Enable or disable contact typing messages notification in console window." })
1542       CMD_NOEXAMPLES
1543     },
1544 
1545     { "/splash",
1546       parse_args, 1, 1, &cons_splash_setting,
1547       CMD_NOSUBFUNCS
1548       CMD_MAINFUNC(cmd_splash)
1549       CMD_TAGS(
1550               CMD_TAG_UI)
1551       CMD_SYN(
1552               "/splash on|off")
1553       CMD_DESC(
1554               "Switch on or off the ascii logo on start up and when the /about command is called.")
1555       CMD_ARGS(
1556               { "on|off", "Enable or disable splash logo." })
1557       CMD_NOEXAMPLES
1558     },
1559 
1560     { "/autoconnect",
1561       parse_args, 1, 2, &cons_autoconnect_setting,
1562       CMD_NOSUBFUNCS
1563       CMD_MAINFUNC(cmd_autoconnect)
1564       CMD_TAGS(
1565               CMD_TAG_CONNECTION)
1566       CMD_SYN(
1567               "/autoconnect set <account>",
1568               "/autoconnect off")
1569       CMD_DESC(
1570               "Enable or disable autoconnect on start up. "
1571               "The setting can be overridden by the -a (--account) command line option.")
1572       CMD_ARGS(
1573               { "set <account>", "Connect with account on start up." },
1574               { "off", "Disable autoconnect." })
1575       CMD_EXAMPLES(
1576               "/autoconnect set ulfhednar@valhalla.edda",
1577               "/autoconnect off")
1578     },
1579 
1580     { "/vercheck",
1581       parse_args, 0, 1, NULL,
1582       CMD_NOSUBFUNCS
1583       CMD_MAINFUNC(cmd_vercheck)
1584       CMD_TAGS(
1585               CMD_TAG_UI)
1586       CMD_SYN(
1587               "/vercheck on|off")
1588       CMD_DESC(
1589               "Check for new versions when Profanity starts, and when the /about command is run.")
1590       CMD_ARGS(
1591               { "on|off", "Enable or disable the version check." })
1592       CMD_NOEXAMPLES
1593     },
1594 
1595     { "/wintitle",
1596       parse_args, 2, 2, &cons_wintitle_setting,
1597       CMD_NOSUBFUNCS
1598       CMD_MAINFUNC(cmd_wintitle)
1599       CMD_TAGS(
1600               CMD_TAG_UI)
1601       CMD_SYN(
1602               "/wintitle show on|off",
1603               "/wintitle goodbye on|off")
1604       CMD_DESC(
1605               "Allow Profanity to modify the window title bar.")
1606       CMD_ARGS(
1607               { "show on|off", "Show current logged in user, and unread messages as the window title." },
1608               { "goodbye on|off", "Show a message in the title when exiting profanity." })
1609       CMD_NOEXAMPLES
1610     },
1611 
1612     { "/alias",
1613       parse_args_with_freetext, 1, 3, NULL,
1614       CMD_NOSUBFUNCS
1615       CMD_MAINFUNC(cmd_alias)
1616       CMD_NOTAGS
1617       CMD_SYN(
1618               "/alias list",
1619               "/alias add <name> <value>",
1620               "/alias remove <name>")
1621       CMD_DESC(
1622               "Add, remove or list command aliases.")
1623       CMD_ARGS(
1624               { "list", "List all aliases." },
1625               { "add <name> <value>", "Add a new command alias." },
1626               { "remove <name>", "Remove a command alias." })
1627       CMD_EXAMPLES(
1628               "/alias add friends /who online friends",
1629               "/alias add /q /quit",
1630               "/alias add a /away \"I'm in a meeting.\"",
1631               "/alias remove q",
1632               "/alias list")
1633     },
1634 
1635     { "/logging",
1636       parse_args, 2, 3, &cons_logging_setting,
1637       CMD_NOSUBFUNCS
1638       CMD_MAINFUNC(cmd_logging)
1639       CMD_TAGS(
1640               CMD_TAG_CHAT)
1641       CMD_SYN(
1642               "/logging chat|group on|off")
1643       CMD_DESC(
1644               "Configure chat logging. "
1645               "Switch logging on or off. "
1646               "Chat logging will be enabled if /history is set to on. "
1647               "When disabling this option, /history will also be disabled. ")
1648       CMD_ARGS(
1649               { "chat on|off", "Enable/Disable regular chat logging." },
1650               { "group on|off", "Enable/Disable groupchat (room) logging." })
1651       CMD_EXAMPLES(
1652               "/logging chat on",
1653               "/logging group off")
1654     },
1655 
1656     { "/states",
1657       parse_args, 1, 1, &cons_states_setting,
1658       CMD_NOSUBFUNCS
1659       CMD_MAINFUNC(cmd_states)
1660       CMD_TAGS(
1661               CMD_TAG_CHAT)
1662       CMD_SYN(
1663               "/states on|off")
1664       CMD_DESC(
1665               "Send chat state notifications to recipient during chat sessions, such as typing, paused, active, gone.")
1666       CMD_ARGS(
1667               { "on|off", "Enable or disable sending of chat state notifications." })
1668       CMD_NOEXAMPLES },
1669 
1670     { "/pgp",
1671       parse_args, 1, 3, NULL,
1672       CMD_NOSUBFUNCS
1673       CMD_MAINFUNC(cmd_pgp)
1674       CMD_TAGS(
1675               CMD_TAG_CHAT,
1676               CMD_TAG_UI)
1677       CMD_SYN(
1678               "/pgp libver",
1679               "/pgp keys",
1680               "/pgp contacts",
1681               "/pgp setkey <contact> <keyid>",
1682               "/pgp start [<contact>]",
1683               "/pgp end",
1684               "/pgp log on|off|redact",
1685               "/pgp char <char>",
1686               "/pgp sendfile on|off")
1687       CMD_DESC(
1688               "Open PGP commands to manage keys, and perform PGP encryption during chat sessions. "
1689               "See the /account command to set your own PGP key.")
1690       CMD_ARGS(
1691               { "libver", "Show which version of the libgpgme library is being used." },
1692               { "keys", "List all keys known to the system." },
1693               { "contacts", "Show contacts with assigned public keys." },
1694               { "setkey <contact> <keyid>", "Manually associate a contact with a public key." },
1695               { "start [<contact>]", "Start PGP encrypted chat, current contact will be used if not specified." },
1696               { "end", "End PGP encrypted chat with the current recipient." },
1697               { "log on|off", "Enable or disable plaintext logging of PGP encrypted messages." },
1698               { "log redact", "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." },
1699               { "char <char>", "Set the character to be displayed next to PGP encrypted messages." },
1700               { "sendfile on|off", "Allow /sendfile to send unencrypted files while otherwise using PGP." })
1701       CMD_EXAMPLES(
1702               "/pgp log off",
1703               "/pgp setkey odin@valhalla.edda BA19CACE5A9592C5",
1704               "/pgp start odin@valhalla.edda",
1705               "/pgp end",
1706               "/pgp char P")
1707     },
1708 
1709 // XEP-0373: OpenPGP for XMPP
1710 #ifdef HAVE_LIBGPGME
1711     { "/ox",
1712       parse_args, 1, 3, NULL,
1713       CMD_NOSUBFUNCS
1714       CMD_MAINFUNC(cmd_ox)
1715       CMD_TAGS(
1716       CMD_TAG_CHAT,
1717       CMD_TAG_UI)
1718       CMD_SYN(
1719               "/ox keys",
1720               "/ox contacts",
1721               "/ox start [<contact>]",
1722               "/ox end",
1723               "/ox log on|off|redact",
1724               "/ox char <char>",
1725               "/ox sendfile on|off",
1726               "/ox announce <file>",
1727               "/ox discover <jid>",
1728               "/ox request <jid> <keyid>")
1729       CMD_DESC(
1730              "OpenPGP (OX) commands to manage keys, and perform OpenPGP encryption during chat sessions. "
1731              "Your OpenPGP key needs a user-id with your JID URI (xmpp:local@domain.tld). "
1732              "A key can be generated with \"gpg --quick-gen-key xmpp:local@domain.tld future-default default 3y\".")
1733       CMD_ARGS(
1734               { "keys", "List all keys known to the system." },
1735               { "contacts", "Show contacts with assigned public keys." },
1736               { "start [<contact>]", "Start PGP encrypted chat, current contact will be used if not specified." },
1737               { "end", "End PGP encrypted chat with the current recipient." },
1738               { "log on|off", "Enable or disable plaintext logging of PGP encrypted messages." },
1739               { "log redact", "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." },
1740               { "char <char>", "Set the character to be displayed next to PGP encrypted messages." },
1741               { "announce <file>", "Announce a public key by pushing it on the XMPP Server" },
1742               { "discover <jid>", "Discover public keys of a jid. The OpenPGP Key IDs will be displayed" },
1743               { "request <jid>", "Request public keys" },
1744               { "sendfile on|off", "Allow /sendfile to send unencrypted files while otherwise using PGP." })
1745       CMD_EXAMPLES(
1746               "/ox log off",
1747               "/ox start odin@valhalla.edda",
1748               "/ox end",
1749               "/ox char X")
1750     },
1751 #endif // HAVE_LIBGPGME
1752 
1753     { "/otr",
1754       parse_args, 1, 3, NULL,
1755       CMD_SUBFUNCS(
1756               { "char", cmd_otr_char },
1757               { "log", cmd_otr_log },
1758               { "libver", cmd_otr_libver },
1759               { "policy", cmd_otr_policy },
1760               { "gen", cmd_otr_gen },
1761               { "myfp", cmd_otr_myfp },
1762               { "theirfp", cmd_otr_theirfp },
1763               { "start", cmd_otr_start },
1764               { "end", cmd_otr_end },
1765               { "trust", cmd_otr_trust },
1766               { "untrust", cmd_otr_untrust },
1767               { "secret", cmd_otr_secret },
1768               { "question", cmd_otr_question },
1769               { "answer", cmd_otr_answer },
1770               { "sendfile", cmd_otr_sendfile })
1771       CMD_NOMAINFUNC
1772       CMD_TAGS(
1773               CMD_TAG_CHAT,
1774               CMD_TAG_UI)
1775       CMD_SYN(
1776               "/otr libver",
1777               "/otr gen",
1778               "/otr myfp|theirfp",
1779               "/otr start [<contact>]",
1780               "/otr end",
1781               "/otr trust|untrust",
1782               "/otr secret <secret>",
1783               "/otr question <question> <answer>",
1784               "/otr answer <answer>",
1785               "/otr policy manual|opportunistic|always [<contact>]",
1786               "/otr log on|off|redact",
1787               "/otr char <char>",
1788               "/otr sendfile on|off")
1789       CMD_DESC(
1790               "Off The Record (OTR) commands to manage keys, and perform OTR encryption during chat sessions.")
1791       CMD_ARGS(
1792               { "libver", "Show which version of the libotr library is being used." },
1793               { "gen", "Generate your private key." },
1794               { "myfp", "Show your fingerprint." },
1795               { "theirfp", "Show contacts fingerprint." },
1796               { "start [<contact>]", "Start an OTR session with contact, or current recipient if omitted." },
1797               { "end", "End the current OTR session." },
1798               { "trust|untrust", "Indicate whether or not you trust the contact's fingerprint." },
1799               { "secret <secret>", "Verify a contact's identity using a shared secret." },
1800               { "question <question> <answer>", "Verify a contact's identity using a question and expected answer." },
1801               { "answer <answer>", "Respond to a question answer verification request with your answer." },
1802               { "policy manual", "Set the global OTR policy to manual, OTR sessions must be started manually." },
1803               { "policy manual <contact>", "Set the OTR policy to manual for a specific contact." },
1804               { "policy opportunistic", "Set the global OTR policy to opportunistic, an OTR session will be attempted upon starting a conversation." },
1805               { "policy opportunistic <contact>", "Set the OTR policy to opportunistic for a specific contact." },
1806               { "policy always", "Set the global OTR policy to always, an error will be displayed if an OTR session cannot be initiated upon starting a conversation." },
1807               { "policy always <contact>", "Set the OTR policy to always for a specific contact." },
1808               { "log on|off", "Enable or disable plaintext logging of OTR encrypted messages." },
1809               { "log redact", "Log OTR encrypted messages, but replace the contents with [redacted]. This is the default." },
1810               { "char <char>", "Set the character to be displayed next to OTR encrypted messages." },
1811               { "sendfile on|off", "Allow /sendfile to send unencrypted files while in an OTR session." })
1812       CMD_EXAMPLES(
1813               "/otr log off",
1814               "/otr policy manual",
1815               "/otr policy opportunistic odin@valhalla.edda",
1816               "/otr gen",
1817               "/otr start odin@valhalla.edda",
1818               "/otr myfp",
1819               "/otr theirfp",
1820               "/otr question \"What is the name of my rabbit?\" fiffi",
1821               "/otr end",
1822               "/otr char *")
1823     },
1824 
1825     { "/outtype",
1826       parse_args, 1, 1, &cons_outtype_setting,
1827       CMD_NOSUBFUNCS
1828       CMD_MAINFUNC(cmd_outtype)
1829       CMD_TAGS(
1830               CMD_TAG_CHAT)
1831       CMD_SYN(
1832               "/outtype on|off")
1833       CMD_DESC(
1834               "Send typing notifications, chat states (/states) will be enabled if this setting is enabled.")
1835       CMD_ARGS(
1836               { "on|off", "Enable or disable sending typing notifications." })
1837       CMD_NOEXAMPLES
1838     },
1839 
1840     { "/gone",
1841       parse_args, 1, 1, &cons_gone_setting,
1842       CMD_NOSUBFUNCS
1843       CMD_MAINFUNC(cmd_gone)
1844       CMD_TAGS(
1845               CMD_TAG_CHAT)
1846       CMD_SYN(
1847               "/gone <minutes>")
1848       CMD_DESC(
1849               "Send a 'gone' state to the recipient after the specified number of minutes. "
1850               "Chat states (/states) will be enabled if this setting is set.")
1851       CMD_ARGS(
1852               { "<minutes>", "Number of minutes of inactivity before sending the 'gone' state, a value of 0 will disable sending this state." })
1853       CMD_NOEXAMPLES
1854     },
1855 
1856     { "/history",
1857       parse_args, 1, 1, &cons_history_setting,
1858       CMD_NOSUBFUNCS
1859       CMD_MAINFUNC(cmd_history)
1860       CMD_TAGS(
1861               CMD_TAG_UI,
1862               CMD_TAG_CHAT)
1863       CMD_SYN(
1864               "/history on|off")
1865       CMD_DESC(
1866               "Switch chat history on or off, /logging chat will automatically be enabled when this setting is on. "
1867               "When history is enabled, previous messages are shown in chat windows.")
1868       CMD_ARGS(
1869               { "on|off", "Enable or disable showing chat history." })
1870       CMD_NOEXAMPLES
1871     },
1872 
1873     { "/log",
1874       parse_args, 1, 2, &cons_log_setting,
1875       CMD_NOSUBFUNCS
1876       CMD_MAINFUNC(cmd_log)
1877       CMD_NOTAGS
1878       CMD_SYN(
1879               "/log where",
1880               "/log rotate on|off",
1881               "/log maxsize <bytes>",
1882               "/log shared on|off")
1883       CMD_DESC(
1884               "Manage profanity log settings.")
1885       CMD_ARGS(
1886               { "where", "Show the current log file location." },
1887               { "rotate on|off", "Rotate log, default on. Does not take effect if you specified a filename yourself when starting Profanity." },
1888               { "maxsize <bytes>", "With rotate enabled, specifies the max log size, defaults to 1048580 (1MB)." },
1889               { "shared on|off", "Share logs between all instances, default: on. When off, the process id will be included in the log filename. Does not take effect if you specified a filename yourself when starting Profanity." })
1890       CMD_NOEXAMPLES
1891     },
1892 
1893     { "/carbons",
1894       parse_args, 1, 1, &cons_carbons_setting,
1895       CMD_NOSUBFUNCS
1896       CMD_MAINFUNC(cmd_carbons)
1897       CMD_TAGS(
1898               CMD_TAG_CHAT)
1899       CMD_SYN(
1900               "/carbons on|off")
1901       CMD_DESC(
1902               "Enable or disable message carbons. "
1903               "Message carbons ensure that both sides of all conversations are shared with all the user's clients that implement this protocol.")
1904       CMD_ARGS(
1905               { "on|off", "Enable or disable message carbons." })
1906       CMD_NOEXAMPLES
1907     },
1908 
1909     { "/receipts",
1910       parse_args, 2, 2, &cons_receipts_setting,
1911       CMD_NOSUBFUNCS
1912       CMD_MAINFUNC(cmd_receipts)
1913       CMD_TAGS(
1914               CMD_TAG_CHAT)
1915       CMD_SYN(
1916               "/receipts request on|off",
1917               "/receipts send on|off")
1918       CMD_DESC(
1919               "Enable or disable message delivery receipts. The interface will indicate when a message has been received.")
1920       CMD_ARGS(
1921               { "request on|off", "Whether or not to request a receipt upon sending a message." },
1922               { "send on|off", "Whether or not to send a receipt if one has been requested with a received message." })
1923       CMD_NOEXAMPLES
1924     },
1925 
1926     { "/reconnect",
1927       parse_args, 1, 1, &cons_reconnect_setting,
1928       CMD_NOSUBFUNCS
1929       CMD_MAINFUNC(cmd_reconnect)
1930       CMD_TAGS(
1931               CMD_TAG_CONNECTION)
1932       CMD_SYN(
1933               "/reconnect <seconds>")
1934       CMD_DESC(
1935               "Set the reconnect attempt interval for when the connection is lost.")
1936       CMD_ARGS(
1937               { "<seconds>", "Number of seconds before attempting to reconnect, a value of 0 disables reconnect." })
1938       CMD_NOEXAMPLES
1939     },
1940 
1941     { "/autoping",
1942       parse_args, 2, 2, &cons_autoping_setting,
1943       CMD_NOSUBFUNCS
1944       CMD_MAINFUNC(cmd_autoping)
1945       CMD_TAGS(
1946               CMD_TAG_CONNECTION)
1947       CMD_SYN(
1948               "/autoping set <seconds>",
1949               "/autoping timeout <seconds>")
1950       CMD_DESC(
1951               "Set the interval between sending ping requests to the server to ensure the connection is kept alive.")
1952       CMD_ARGS(
1953               { "set <seconds>", "Number of seconds between sending pings, a value of 0 disables autoping." },
1954               { "timeout <seconds>", "Seconds to wait for autoping responses, after which the connection is considered broken." })
1955       CMD_NOEXAMPLES
1956     },
1957 
1958     { "/ping",
1959       parse_args, 0, 1, NULL,
1960       CMD_NOSUBFUNCS
1961       CMD_MAINFUNC(cmd_ping)
1962       CMD_TAGS(
1963               CMD_TAG_CONNECTION)
1964       CMD_SYN(
1965               "/ping [<jid>]")
1966       CMD_DESC(
1967               "Sends an IQ ping stanza to the specified JID. "
1968               "If no JID is supplied, your chat server will be pinged.")
1969       CMD_ARGS(
1970               { "<jid>", "The Jabber ID to send the ping request to." })
1971       CMD_NOEXAMPLES
1972     },
1973 
1974     { "/autoaway",
1975       parse_args_with_freetext, 2, 3, &cons_autoaway_setting,
1976       CMD_NOSUBFUNCS
1977       CMD_MAINFUNC(cmd_autoaway)
1978       CMD_TAGS(
1979               CMD_TAG_PRESENCE)
1980       CMD_SYN(
1981               "/autoaway mode idle|away|off",
1982               "/autoaway time away|xa <minutes>",
1983               "/autoaway message away|xa <message>|off",
1984               "/autoaway check on|off")
1985       CMD_DESC(
1986               "Manage autoaway settings for idle time.")
1987       CMD_ARGS(
1988               { "mode idle", "Sends idle time, status remains online." },
1989               { "mode away", "Sends away and xa presence as well as idle time." },
1990               { "mode off", "Disabled (default)." },
1991               { "time away <minutes>", "Number of minutes before the away presence is sent, default: 15." },
1992               { "time xa <minutes>", "Number of minutes before the xa presence is sent, default: 0 (disabled)." },
1993               { "message away <message>", "Optional message to send with the away presence, default: off (disabled)." },
1994               { "message xa <message>", "Optional message to send with the xa presence, default: off (disabled)." },
1995               { "message away off", "Send no message with away presence." },
1996               { "message xa off", "Send no message with xa presence." },
1997               { "check on|off", "When enabled, checks for activity and sends online presence, default: on." })
1998       CMD_EXAMPLES(
1999               "/autoaway mode away",
2000               "/autoaway time away 30",
2001               "/autoaway message away Away from computer for a while",
2002               "/autoaway time xa 120",
2003               "/autoaway message xa Away from computer for a very long time",
2004               "/autoaway check off")
2005     },
2006 
2007     { "/priority",
2008       parse_args, 1, 1, NULL,
2009       CMD_NOSUBFUNCS
2010       CMD_MAINFUNC(cmd_priority)
2011       CMD_TAGS(
2012               CMD_TAG_PRESENCE)
2013       CMD_SYN(
2014               "/priority <priority>")
2015       CMD_DESC(
2016               "Set priority for the current account. "
2017               "See the /account command for specific priority settings per presence status.")
2018       CMD_ARGS(
2019               { "<priority>", "Number between -128 and 127, default: 0." })
2020       CMD_NOEXAMPLES
2021     },
2022 
2023     { "/account",
2024       parse_args, 0, 4, NULL,
2025       CMD_SUBFUNCS(
2026               { "list", cmd_account_list },
2027               { "show", cmd_account_show },
2028               { "add", cmd_account_add },
2029               { "remove", cmd_account_remove },
2030               { "enable", cmd_account_enable },
2031               { "disable", cmd_account_disable },
2032               { "rename", cmd_account_rename },
2033               { "default", cmd_account_default },
2034               { "set", cmd_account_set },
2035               { "clear", cmd_account_clear })
2036       CMD_MAINFUNC(cmd_account)
2037       CMD_TAGS(
2038               CMD_TAG_CONNECTION,
2039               CMD_TAG_PRESENCE,
2040               CMD_TAG_CHAT,
2041               CMD_TAG_GROUPCHAT)
2042       CMD_SYN(
2043               "/account",
2044               "/account list",
2045               "/account show <account>",
2046               "/account enable|disable <account>",
2047               "/account default set <account>",
2048               "/account default off",
2049               "/account add <account>",
2050               "/account remove <account>",
2051               "/account rename <account> <newaccount>",
2052               "/account set <account> jid <jid>",
2053               "/account set <account> server <server>",
2054               "/account set <account> port <port>",
2055               "/account set <account> status <presence>",
2056               "/account set <account> status last",
2057               "/account set <account> <presence> <priority>",
2058               "/account set <account> resource <resource>",
2059               "/account set <account> password <password>",
2060               "/account set <account> eval_password <command>",
2061               "/account set <account> muc <service>",
2062               "/account set <account> nick <nick>",
2063               "/account set <account> otr <policy>",
2064               "/account set <account> pgpkeyid <pgpkeyid>",
2065               "/account set <account> startscript <script>",
2066               "/account set <account> tls force|allow|trust|legacy|disable",
2067               "/account set <account> auth default|legacy",
2068               "/account set <account> theme <theme>",
2069               "/account clear <account> password",
2070               "/account clear <account> eval_password",
2071               "/account clear <account> server",
2072               "/account clear <account> port",
2073               "/account clear <account> otr",
2074               "/account clear <account> pgpkeyid",
2075               "/account clear <account> startscript",
2076               "/account clear <account> muc",
2077               "/account clear <account> resource")
2078       CMD_DESC(
2079               "Commands for creating and managing accounts. "
2080               "Calling with no arguments will display information for the current account.")
2081       CMD_ARGS(
2082               { "list", "List all accounts." },
2083               { "enable <account>", "Enable the account, it will be used for autocompletion." },
2084               { "show <account>", "Show details for the specified account." },
2085               { "disable <account>", "Disable the account." },
2086               { "default set <account>", "Set the default account, used when no argument passed to the /connect command." },
2087               { "default off", "Clear the default account setting." },
2088               { "add <account>", "Create a new account." },
2089               { "remove <account>", "Remove an account." },
2090               { "rename <account> <newaccount>", "Rename 'account' to 'newaccount'." },
2091               { "set <account> jid <jid>", "Set the Jabber ID for the account, account name will be used if not set." },
2092               { "set <account> server <server>", "The chat server, if different to the domainpart of the JID." },
2093               { "set <account> port <port>", "The port used for connecting if not the default (5222, or 5223 for SSL)." },
2094               { "set <account> status <presence>", "The presence status to use on login." },
2095               { "set <account> status last", "Use your last status before logging out, when logging in." },
2096               { "set <account> <presence> <priority>", "Set the priority (-128..127) to use for the specified presence." },
2097               { "set <account> resource <resource>", "The resource to be used for this account, defaults to 'profanity'." },
2098               { "set <account> password <password>", "Password for the account, note this is currently stored in plaintext if set." },
2099               { "set <account> eval_password <command>", "Shell command evaluated to retrieve password for the account. Can be used to retrieve password from keyring." },
2100               { "set <account> muc <service>", "The default MUC chat service to use, defaults to the servers disco info response." },
2101               { "set <account> nick <nick>", "The default nickname to use when joining chat rooms." },
2102               { "set <account> otr <policy>", "Override global OTR policy for this account, see /otr." },
2103               { "set <account> pgpkeyid <pgpkeyid>", "Set the ID of the PGP key for this account, see /pgp." },
2104               { "set <account> startscript <script>", "Set the script to execute after connecting." },
2105               { "set <account> tls force", "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
2106               { "set <account> tls allow", "Use TLS for the connection if it is available." },
2107               { "set <account> tls trust", "Force TLS connection and trust server's certificate." },
2108               { "set <account> tls legacy", "Use legacy TLS for the connection. It means server doesn't support STARTTLS and TLS is forced just after TCP connection is established." },
2109               { "set <account> tls disable", "Disable TLS for the connection." },
2110               { "set <account> auth default", "Use default authentication process." },
2111               { "set <account> auth legacy", "Allow legacy authentication." },
2112               { "set <account> <theme>", "Set the UI theme for the account." },
2113               { "clear <account> server", "Remove the server setting for this account." },
2114               { "clear <account> port", "Remove the port setting for this account." },
2115               { "clear <account> password", "Remove the password setting for this account." },
2116               { "clear <account> eval_password", "Remove the eval_password setting for this account." },
2117               { "clear <account> otr", "Remove the OTR policy setting for this account." },
2118               { "clear <account> pgpkeyid", "Remove pgpkeyid associated with this account." },
2119               { "clear <account> startscript", "Remove startscript associated with this account." },
2120               { "clear <account> theme", "Clear the theme setting for the account, the global theme will be used." },
2121               { "clear <account> resource", "Remove the resource setting for this account." },
2122               { "clear <account> muc", "Remove the default MUC service setting." })
2123       CMD_EXAMPLES(
2124               "/account add me",
2125               "/account set me jid ulfhednar@valhalla.edda",
2126               "/account set me server talk.chat.com",
2127               "/account set me port 5111",
2128               "/account set me muc chatservice.mycompany.com",
2129               "/account set me nick dennis",
2130               "/account set me status dnd",
2131               "/account set me dnd -1",
2132               "/account rename me chattyme",
2133               "/account clear me pgpkeyid")
2134     },
2135 
2136     { "/plugins",
2137       parse_args, 0, 3, NULL,
2138       CMD_SUBFUNCS(
2139               { "sourcepath", cmd_plugins_sourcepath },
2140               { "install", cmd_plugins_install },
2141               { "uninstall", cmd_plugins_uninstall },
2142               { "update", cmd_plugins_update },
2143               { "load", cmd_plugins_load },
2144               { "unload", cmd_plugins_unload },
2145               { "reload", cmd_plugins_reload },
2146               { "python_version", cmd_plugins_python_version })
2147       CMD_MAINFUNC(cmd_plugins)
2148       CMD_NOTAGS
2149       CMD_SYN(
2150               "/plugins",
2151               "/plugins sourcepath set <path>",
2152               "/plugins sourcepath clear",
2153               "/plugins install [<path>]",
2154               "/plugins uninstall [<plugin>]",
2155               "/plugins update [<path>]",
2156               "/plugins unload [<plugin>]",
2157               "/plugins load [<plugin>]",
2158               "/plugins reload [<plugin>]",
2159               "/plugins python_version")
2160       CMD_DESC(
2161               "Manage plugins. Passing no arguments lists currently loaded plugins.")
2162       CMD_ARGS(
2163               { "sourcepath set <path>", "Set the default path to install plugins from, will be used if no arg is passed to /plugins install." },
2164               { "sourcepath clear", "Clear the default plugins source path." },
2165               { "install [<path>]", "Install a plugin, or all plugins found in a directory (recursive). Passing no argument will use the sourcepath if one is set." },
2166               { "uninstall [<plugin>]", "Uninstall a plugin." },
2167               { "update [<path>]", "Updates an installed plugin" },
2168               { "load [<plugin>]", "Load a plugin that already exists in the plugin directory, passing no argument loads all found plugins." },
2169               { "unload [<plugin>]", "Unload a loaded plugin, passing no argument will unload all plugins." },
2170               { "reload [<plugin>]", "Reload a plugin, passing no argument will reload all plugins." },
2171               { "python_version", "Show the Python interpreter version." })
2172       CMD_EXAMPLES(
2173               "/plugins sourcepath set /home/meee/projects/profanity-plugins",
2174               "/plugins install",
2175               "/plugins install /home/steveharris/Downloads/metal.py",
2176               "/plugins update /home/steveharris/Downloads/metal.py",
2177               "/plugins uninstall browser.py",
2178               "/plugins load browser.py",
2179               "/plugins unload say.py",
2180               "/plugins reload wikipedia.py")
2181     },
2182 
2183     { "/prefs",
2184       parse_args, 0, 1, NULL,
2185       CMD_NOSUBFUNCS
2186       CMD_MAINFUNC(cmd_prefs)
2187       CMD_NOTAGS
2188       CMD_SYN(
2189               "/prefs [ui|desktop|chat|log|conn|presence|otr|pgp|omemo]")
2190       CMD_DESC(
2191               "Show preferences for different areas of functionality. "
2192               "Passing no arguments shows all preferences.")
2193       CMD_ARGS(
2194               { "ui", "User interface preferences." },
2195               { "desktop", "Desktop notification preferences." },
2196               { "chat", "Chat state preferences." },
2197               { "log", "Logging preferences." },
2198               { "conn", "Connection handling preferences." },
2199               { "presence", "Chat presence preferences." },
2200               { "otr", "Off The Record preferences." },
2201               { "pgp", "OpenPGP preferences." },
2202               { "omemo", "OMEMO preferences." })
2203       CMD_NOEXAMPLES
2204     },
2205 
2206     { "/theme",
2207       parse_args, 1, 2, &cons_theme_setting,
2208       CMD_NOSUBFUNCS
2209       CMD_MAINFUNC(cmd_theme)
2210       CMD_TAGS(
2211               CMD_TAG_UI)
2212       CMD_SYN(
2213               "/theme list",
2214               "/theme load <theme>",
2215               "/theme full-load <theme>",
2216               "/theme colours",
2217               "/theme properties")
2218       CMD_DESC(
2219               "Load a theme, includes colours and UI options.")
2220       CMD_ARGS(
2221               { "list", "List all available themes." },
2222               { "load <theme>", "Load colours from specified theme. 'default' will reset to the default theme." },
2223               { "full-load <theme>", "Same as 'load' but will also load preferences set in the theme, not just colours." },
2224               { "colours", "Show colour values as rendered by the terminal." },
2225               { "properties", "Show colour settings for current theme." })
2226       CMD_EXAMPLES(
2227               "/theme list",
2228               "/theme load forest")
2229     },
2230 
2231     { "/xmlconsole",
2232       parse_args, 0, 0, NULL,
2233       CMD_NOSUBFUNCS
2234       CMD_MAINFUNC(cmd_xmlconsole)
2235       CMD_TAGS(
2236               CMD_TAG_UI)
2237       CMD_SYN(
2238               "/xmlconsole")
2239       CMD_DESC(
2240               "Open the XML console to view incoming and outgoing XMPP traffic.")
2241       CMD_NOARGS
2242       CMD_NOEXAMPLES
2243     },
2244 
2245     { "/script",
2246       parse_args, 1, 2, NULL,
2247       CMD_NOSUBFUNCS
2248       CMD_MAINFUNC(cmd_script)
2249       CMD_NOTAGS
2250       CMD_SYN(
2251               "/script run <script>",
2252               "/script list",
2253               "/script show <script>")
2254       CMD_DESC(
2255               "Run command scripts. "
2256               "Scripts are stored in $XDG_DATA_HOME/profanity/scripts/ which is usually $HOME/.local/share/profanity/scripts/.")
2257       CMD_ARGS(
2258               { "script run <script>", "Execute a script." },
2259               { "script list", "List all scripts TODO." },
2260               { "script show <script>", "Show the commands in script TODO." })
2261       CMD_EXAMPLES(
2262               "/script list",
2263               "/script run myscript",
2264               "/script show somescript")
2265     },
2266 
2267     { "/export",
2268       parse_args, 1, 1, NULL,
2269       CMD_NOSUBFUNCS
2270       CMD_MAINFUNC(cmd_export)
2271       CMD_NOTAGS
2272       CMD_SYN(
2273               "/export <filepath>")
2274       CMD_DESC(
2275               "Exports contacts to a csv file.")
2276       CMD_ARGS(
2277               { "<filepath>", "Path to the output file." })
2278       CMD_EXAMPLES(
2279               "/export /path/to/output.csv",
2280               "/export ~/contacts.csv")
2281     },
2282 
2283     { "/cmd",
2284       parse_args, 1, 3, NULL,
2285       CMD_SUBFUNCS(
2286               { "list", cmd_command_list },
2287               { "exec", cmd_command_exec })
2288       CMD_NOMAINFUNC
2289       CMD_NOTAGS
2290       CMD_SYN(
2291               "/cmd list [<jid>]",
2292               "/cmd exec <command> [<jid>]")
2293       CMD_DESC(
2294               "Execute ad hoc commands.")
2295       CMD_ARGS(
2296               { "list", "List supported ad hoc commands." },
2297               { "exec <command>", "Execute a command." })
2298       CMD_EXAMPLES(
2299               "/cmd list",
2300               "/cmd exec ping")
2301     },
2302 
2303     { "/omemo",
2304         parse_args, 1, 3, NULL,
2305         CMD_SUBFUNCS(
2306             { "gen", cmd_omemo_gen },
2307             { "log", cmd_omemo_log },
2308             { "start", cmd_omemo_start },
2309             { "end", cmd_omemo_end },
2310             { "trustmode", cmd_omemo_trust_mode },
2311             { "trust", cmd_omemo_trust },
2312             { "untrust", cmd_omemo_untrust },
2313             { "fingerprint", cmd_omemo_fingerprint },
2314             { "char", cmd_omemo_char },
2315             { "policy", cmd_omemo_policy },
2316             { "clear_device_list", cmd_omemo_clear_device_list })
2317         CMD_NOMAINFUNC
2318         CMD_TAGS(
2319             CMD_TAG_CHAT,
2320             CMD_TAG_UI)
2321         CMD_SYN(
2322             "/omemo gen",
2323             "/omemo log on|off|redact",
2324             "/omemo start [<contact>]",
2325             "/omemo trust [<contact>] <fingerprint>",
2326             "/omemo end",
2327             "/omemo fingerprint [<contact>]",
2328             "/omemo char <char>",
2329             "/omemo trustmode manual|firstusage|blind",
2330             "/omemo policy manual|automatic|always",
2331             "/omemo clear_device_list")
2332         CMD_DESC(
2333             "OMEMO commands to manage keys, and perform encryption during chat sessions.")
2334         CMD_ARGS(
2335             { "gen",                     "Generate OMEMO crytographic materials for current account." },
2336             { "start [<contact>]",       "Start an OMEMO session with contact, or current recipient if omitted." },
2337             { "end",                     "End the current OMEMO session." },
2338             { "log on|off",              "Enable or disable plaintext logging of OMEMO encrypted messages." },
2339             { "log redact",              "Log OMEMO encrypted messages, but replace the contents with [redacted]. This is the default." },
2340             { "fingerprint [<contact>]", "Show contact fingerprints, or current recipient if omitted." },
2341             { "char <char>",             "Set the character to be displayed next to OMEMO encrypted messages." },
2342             { "trustmode manual",        "Set the global OMEMO trust mode to manual, OMEMO keys has to be trusted manually." },
2343             { "trustmode firstusage",    "Set the global OMEMO trust mode to ToFu, first OMEMO keys trusted automatically." },
2344             { "trustmode blind",         "Set the global OMEMO trust mode to blind, ALL OMEMO keys trusted automatically." },
2345             { "policy manual",           "Set the global OMEMO policy to manual, OMEMO sessions must be started manually." },
2346             { "policy automatic",        "Set the global OMEMO policy to opportunistic, an OMEMO session will be attempted upon starting a conversation." },
2347             { "policy always",           "Set the global OMEMO policy to always, an error will be displayed if an OMEMO session cannot be initiated upon starting a conversation." },
2348             { "clear_device_list",       "Clear your own device list on server side. Each client will reannounce itself when connected back."})
2349         CMD_EXAMPLES(
2350             "/omemo gen",
2351             "/omemo start odin@valhalla.edda",
2352             "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a",
2353             "/omemo untrust loki@valhalla.edda c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a",
2354             "/omemo char *")
2355     },
2356 
2357     { "/save",
2358       parse_args, 0, 0, NULL,
2359       CMD_NOSUBFUNCS
2360       CMD_MAINFUNC(cmd_save)
2361       CMD_NOTAGS
2362       CMD_SYN(
2363               "/save")
2364       CMD_DESC(
2365               "Save preferences to configuration file.")
2366       CMD_NOARGS
2367       CMD_NOEXAMPLES
2368     },
2369 
2370     { "/reload",
2371       parse_args, 0, 0, NULL,
2372       CMD_NOSUBFUNCS
2373       CMD_MAINFUNC(cmd_reload)
2374       CMD_NOTAGS
2375       CMD_SYN(
2376               "/reload")
2377       CMD_DESC(
2378               "Reload preferences from configuration file.")
2379       CMD_NOARGS
2380       CMD_NOEXAMPLES
2381     },
2382 
2383     { "/paste",
2384       parse_args, 0, 0, NULL,
2385       CMD_NOSUBFUNCS
2386       CMD_MAINFUNC(cmd_paste)
2387       CMD_NOTAGS
2388       CMD_SYN(
2389               "/paste")
2390       CMD_DESC(
2391               "Paste clipboard.")
2392       CMD_NOARGS
2393       CMD_NOEXAMPLES
2394     },
2395 
2396     { "/color",
2397       parse_args, 1, 2, &cons_color_setting,
2398       CMD_NOSUBFUNCS
2399       CMD_MAINFUNC(cmd_color)
2400       CMD_TAGS(
2401               CMD_TAG_UI)
2402       CMD_SYN(
2403               "/color on|off|redgreen|blue",
2404               "/color own on|off")
2405       CMD_DESC(
2406               "Settings for consistent color generation for nicks (XEP-0392). Including corrections for Color Vision Deficiencies. "
2407               "Your terminal needs to support 256 colors.")
2408       CMD_ARGS(
2409               { "on|off|redgreen|blue", "Enable or disable nick colorization for MUC nicks. 'redgreen' is for people with red/green blindness and 'blue' for people with blue blindness." },
2410               { "own on|off", "Enable color generation for own nick. If disabled the color from the color from the theme ('me') will get used." })
2411       CMD_EXAMPLES(
2412               "/color off",
2413               "/color on",
2414               "/color blue",
2415               "/color own off")
2416     },
2417 
2418     { "/avatar",
2419       parse_args, 2, 2, NULL,
2420       CMD_NOSUBFUNCS
2421       CMD_MAINFUNC(cmd_avatar)
2422       CMD_TAGS(
2423               CMD_TAG_CHAT)
2424       CMD_SYN(
2425               "/avatar get <barejid>",
2426               "/avatar open <barejid>")
2427       CMD_DESC(
2428               "Download avatar (XEP-0084) for a certain contact. "
2429               "If nothing happens after using this command the user either doesn't have an avatar set at all "
2430               "or doesn't use XEP-0084 to publish it.")
2431       CMD_ARGS(
2432               { "get <barejid>", "Download the avatar. barejid is the JID to download avatar from." },
2433               { "open <barejid>", "Download avatar and open it with command." })
2434       CMD_EXAMPLES(
2435               "/avatar get thor@valhalla.edda",
2436               "/avatar open freyja@vanaheimr.edda") },
2437 
2438     { "/os",
2439       parse_args, 1, 1, &cons_os_setting,
2440       CMD_NOSUBFUNCS
2441       CMD_MAINFUNC(cmd_os)
2442       CMD_TAGS(
2443               CMD_TAG_DISCOVERY)
2444       CMD_SYN(
2445               "/os <on>|<off>")
2446       CMD_DESC(
2447               "Choose whether to include the OS name if a user asks for software information (XEP-0092).")
2448       CMD_ARGS(
2449               { "on|off", "" })
2450       CMD_NOEXAMPLES
2451     },
2452 
2453     { "/correction",
2454       parse_args, 1, 2, &cons_correction_setting,
2455       CMD_NOSUBFUNCS
2456       CMD_MAINFUNC(cmd_correction)
2457       CMD_TAGS(
2458               CMD_TAG_UI,
2459               CMD_TAG_CHAT,
2460               CMD_TAG_GROUPCHAT)
2461       CMD_SYN(
2462               "/correction <on>|<off>",
2463               "/correction char <char>")
2464       CMD_DESC(
2465               "Settings regarding Last Message Correction (XEP-0308). "
2466               "Corrections will only work in MUC and regular chat windows. MUC PMs won't be allowed. "
2467               "For more information on how to correct messages, see: /help correct.")
2468       CMD_ARGS(
2469               { "on|off", "Enable/Disable support for last message correction." },
2470               { "char", "Set character that will prefix corrected messages. Default: '+'." })
2471       CMD_NOEXAMPLES
2472     },
2473 
2474     { "/correct",
2475       parse_args_as_one, 1, 1, NULL,
2476       CMD_NOSUBFUNCS
2477       CMD_MAINFUNC(cmd_correct)
2478       CMD_TAGS(
2479               CMD_TAG_CHAT,
2480               CMD_TAG_GROUPCHAT)
2481       CMD_SYN(
2482               "/correct <message>")
2483       CMD_DESC(
2484               "Correct and resend the last message (XEP-0308). "
2485               "Use tab completion to get the last sent message. "
2486               "For more information on how to configure corrections, see: /help correction.")
2487       CMD_ARGS(
2488               { "message", "The corrected message." })
2489       CMD_NOEXAMPLES
2490     },
2491 
2492     { "/slashguard",
2493       parse_args, 1, 1, &cons_slashguard_setting,
2494       CMD_NOSUBFUNCS
2495       CMD_MAINFUNC(cmd_slashguard)
2496       CMD_TAGS(
2497               CMD_TAG_UI,
2498               CMD_TAG_CHAT)
2499       CMD_SYN(
2500               "/slashguard on|off")
2501       CMD_DESC(
2502               "Slashguard won't accept a slash in the first 4 characters of your input field. "
2503               "It tries to protect you from typing ' /quit' and similar things in chats.")
2504       CMD_ARGS(
2505               { "on|off", "Enable or disable slashguard." })
2506       CMD_NOEXAMPLES
2507     },
2508 
2509     { "/serversoftware",
2510       parse_args, 1, 1, NULL,
2511       CMD_NOSUBFUNCS
2512       CMD_MAINFUNC(cmd_serversoftware)
2513       CMD_TAGS(
2514               CMD_TAG_DISCOVERY)
2515       CMD_SYN(
2516               "/serversoftware <domain>")
2517       CMD_DESC(
2518               "Find server or component software version information.")
2519       CMD_ARGS(
2520               { "<domain>", "The jid of your server or component." })
2521       CMD_EXAMPLES(
2522               "/serversoftware valhalla.edda",
2523               "/serversoftware xmpp.vanaheimr.edda")
2524     },
2525 
2526     { "/executable",
2527       parse_args, 2, 4, &cons_executable_setting,
2528       CMD_SUBFUNCS(
2529               { "avatar",  cmd_executable_avatar },
2530               { "urlopen", cmd_executable_urlopen },
2531               { "urlsave", cmd_executable_urlsave },
2532               { "editor", cmd_executable_editor })
2533       CMD_NOMAINFUNC
2534       CMD_TAGS(
2535               CMD_TAG_DISCOVERY)
2536       CMD_SYN(
2537               "/executable avatar <cmd>",
2538               "/executable urlopen set <cmdtemplate>",
2539               "/executable urlopen default",
2540               "/executable urlsave set <cmdtemplate>",
2541               "/executable urlsave default")
2542       CMD_DESC(
2543               "Configure executable that should be called upon a certain command.")
2544       CMD_ARGS(
2545               { "avatar", "Set executable that is run by /avatar open. Use your favorite image viewer." },
2546               { "urlopen set", "Set executable that is run by /url open. Takes a command template that replaces %u and %p with the URL and path respectively." },
2547               { "urlopen default", "Restore to default settings." },
2548               { "urlsave set", "Set executable that is run by /url save. Takes a command template that replaces %u and %p with the URL and path respectively." },
2549               { "urlsave default", "Use the built-in download method for saving." },
2550               { "editor set", "Set editor to be used with /editor. Needs a terminal editor or a script to run a graphical editor." })
2551       CMD_EXAMPLES(
2552               "/executable avatar xdg-open",
2553               "/executable urlopen set \"xdg-open %u\"",
2554               "/executable urlopen set \"firefox %u\"",
2555               "/executable urlopen default",
2556               "/executable urlsave set \"wget %u -O %p\"",
2557               "/executable urlsave set \"curl %u -o %p\"",
2558               "/executable urlsave default",
2559               "/executable editor set vim")
2560     },
2561 
2562     { "/url",
2563       parse_args, 2, 3, NULL,
2564       CMD_SUBFUNCS(
2565               { "open", cmd_url_open },
2566               { "save", cmd_url_save })
2567       CMD_NOMAINFUNC
2568       CMD_TAGS(
2569               CMD_TAG_CHAT,
2570               CMD_TAG_GROUPCHAT)
2571       CMD_SYN(
2572               "/url open <url>",
2573               "/url save <url> [<path>]")
2574       CMD_DESC(
2575               "Deal with URLs")
2576       CMD_ARGS(
2577               { "open", "Open URL with predefined executable." },
2578               { "save", "Save URL to optional path, default path is current directory" })
2579       CMD_EXAMPLES(
2580               "/url open https://profanity-im.github.io",
2581               "/url save https://profanity-im.github.io/guide/latest/userguide.html /home/user/Download/")
2582     },
2583 
2584     { "/mam",
2585       parse_args, 1, 1, &cons_mam_setting,
2586       CMD_NOSUBFUNCS
2587       CMD_MAINFUNC(cmd_mam)
2588       CMD_TAGS(
2589               CMD_TAG_CHAT)
2590       CMD_SYN(
2591               "/mam <on>|<off>")
2592       CMD_DESC(
2593               "Enable/Disable Message Archive Management (XEP-0313) "
2594               "MAM is in experimental state. For regular users there are still many confusing things when enabling this feature. "
2595               "We are going to work on this in future releases. So far this setting is mostly here for developers.")
2596       CMD_ARGS(
2597               { "on|off", "Enable or disable MAM" })
2598       CMD_NOEXAMPLES
2599     },
2600 
2601     { "/changepassword",
2602       parse_args, 0, 0, NULL,
2603       CMD_NOSUBFUNCS
2604       CMD_MAINFUNC(cmd_change_password)
2605       CMD_NOTAGS
2606       CMD_SYN(
2607               "/changepassword")
2608       CMD_DESC(
2609               "Change password of logged in account")
2610       CMD_NOARGS
2611       CMD_NOEXAMPLES
2612     },
2613 
2614     { "/editor",
2615       parse_args, 0, 0, NULL,
2616       CMD_NOSUBFUNCS
2617       CMD_MAINFUNC(cmd_editor)
2618       CMD_TAGS(
2619               CMD_TAG_CHAT,
2620               CMD_TAG_GROUPCHAT)
2621       CMD_SYN(
2622               "/editor")
2623       CMD_DESC(
2624               "Spawn external editor to edit message. "
2625               "After editing the inputline may appear empty. Press enter to send the text anyways. "
2626               "Use /executable to set your favourite editor." )
2627       CMD_NOARGS
2628       CMD_NOEXAMPLES
2629     },
2630 
2631     { "/silence",
2632       parse_args, 1, 1, &cons_silence_setting,
2633       CMD_NOSUBFUNCS
2634       CMD_MAINFUNC(cmd_silence)
2635       CMD_TAGS(
2636               CMD_TAG_CHAT)
2637       CMD_SYN(
2638               "/silence on|off")
2639       CMD_DESC(
2640               "Let's you silence all message attempts from people who are not in your roster.")
2641       CMD_NOARGS
2642       CMD_NOEXAMPLES
2643     },
2644 
2645     // NEXT-COMMAND (search helper)
2646 };
2647 
2648 // clang-format on
2649 
2650 static GHashTable* search_index;
2651 
2652 char*
_cmd_index(Command * cmd)2653 _cmd_index(Command* cmd)
2654 {
2655     GString* index_source = g_string_new("");
2656     index_source = g_string_append(index_source, cmd->cmd);
2657     index_source = g_string_append(index_source, " ");
2658     index_source = g_string_append(index_source, cmd->help.desc);
2659     index_source = g_string_append(index_source, " ");
2660 
2661     int len = g_strv_length(cmd->help.tags);
2662     for (int i = 0; i < len; i++) {
2663         index_source = g_string_append(index_source, cmd->help.tags[i]);
2664         index_source = g_string_append(index_source, " ");
2665     }
2666     len = g_strv_length(cmd->help.synopsis);
2667     for (int i = 0; i < len; i++) {
2668         index_source = g_string_append(index_source, cmd->help.synopsis[i]);
2669         index_source = g_string_append(index_source, " ");
2670     }
2671     for (int i = 0; cmd->help.args[i][0] != NULL; i++) {
2672         index_source = g_string_append(index_source, cmd->help.args[i][0]);
2673         index_source = g_string_append(index_source, " ");
2674         index_source = g_string_append(index_source, cmd->help.args[i][1]);
2675         index_source = g_string_append(index_source, " ");
2676     }
2677 
2678     gchar** tokens = g_str_tokenize_and_fold(index_source->str, NULL, NULL);
2679     g_string_free(index_source, TRUE);
2680 
2681     GString* index = g_string_new("");
2682     for (int i = 0; i < g_strv_length(tokens); i++) {
2683         index = g_string_append(index, tokens[i]);
2684         index = g_string_append(index, " ");
2685     }
2686     g_strfreev(tokens);
2687 
2688     char* res = index->str;
2689     g_string_free(index, FALSE);
2690 
2691     return res;
2692 }
2693 
2694 GList*
cmd_search_index_any(char * term)2695 cmd_search_index_any(char* term)
2696 {
2697     GList* results = NULL;
2698 
2699     gchar** processed_terms = g_str_tokenize_and_fold(term, NULL, NULL);
2700     int terms_len = g_strv_length(processed_terms);
2701 
2702     for (int i = 0; i < terms_len; i++) {
2703         GList* index_keys = g_hash_table_get_keys(search_index);
2704         GList* curr = index_keys;
2705         while (curr) {
2706             char* index_entry = g_hash_table_lookup(search_index, curr->data);
2707             if (g_str_match_string(processed_terms[i], index_entry, FALSE)) {
2708                 results = g_list_append(results, curr->data);
2709             }
2710             curr = g_list_next(curr);
2711         }
2712         g_list_free(index_keys);
2713     }
2714 
2715     g_strfreev(processed_terms);
2716 
2717     return results;
2718 }
2719 
2720 GList*
cmd_search_index_all(char * term)2721 cmd_search_index_all(char* term)
2722 {
2723     GList* results = NULL;
2724 
2725     gchar** terms = g_str_tokenize_and_fold(term, NULL, NULL);
2726     int terms_len = g_strv_length(terms);
2727 
2728     GList* commands = g_hash_table_get_keys(search_index);
2729     GList* curr = commands;
2730     while (curr) {
2731         char* command = curr->data;
2732         int matches = 0;
2733         for (int i = 0; i < terms_len; i++) {
2734             char* command_index = g_hash_table_lookup(search_index, command);
2735             if (g_str_match_string(terms[i], command_index, FALSE)) {
2736                 matches++;
2737             }
2738         }
2739         if (matches == terms_len) {
2740             results = g_list_append(results, command);
2741         }
2742         curr = g_list_next(curr);
2743     }
2744 
2745     g_list_free(commands);
2746     g_strfreev(terms);
2747 
2748     return results;
2749 }
2750 
2751 /*
2752  * Initialise command autocompleter and history
2753  */
2754 void
cmd_init(void)2755 cmd_init(void)
2756 {
2757     log_info("Initialising commands");
2758 
2759     cmd_ac_init();
2760 
2761     search_index = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_free);
2762 
2763     // load command defs into hash table
2764     commands = g_hash_table_new(g_str_hash, g_str_equal);
2765     for (unsigned int i = 0; i < ARRAY_SIZE(command_defs); i++) {
2766         Command* pcmd = command_defs + i;
2767 
2768         // add to hash
2769         g_hash_table_insert(commands, pcmd->cmd, pcmd);
2770 
2771         // add to search index
2772         g_hash_table_insert(search_index, strdup(pcmd->cmd), _cmd_index(pcmd));
2773 
2774         // add to commands and help autocompleters
2775         cmd_ac_add_cmd(pcmd);
2776     }
2777 
2778     // load aliases
2779     GList* aliases = prefs_get_aliases();
2780     GList* curr = aliases;
2781     while (curr) {
2782         ProfAlias* alias = curr->data;
2783         cmd_ac_add_alias(alias);
2784         curr = g_list_next(curr);
2785     }
2786     prefs_free_aliases(aliases);
2787 }
2788 
2789 void
cmd_uninit(void)2790 cmd_uninit(void)
2791 {
2792     cmd_ac_uninit();
2793     g_hash_table_destroy(search_index);
2794 }
2795 
2796 gboolean
cmd_valid_tag(const char * const str)2797 cmd_valid_tag(const char* const str)
2798 {
2799     return ((g_strcmp0(str, CMD_TAG_CHAT) == 0) || (g_strcmp0(str, CMD_TAG_GROUPCHAT) == 0) || (g_strcmp0(str, CMD_TAG_PRESENCE) == 0) || (g_strcmp0(str, CMD_TAG_ROSTER) == 0) || (g_strcmp0(str, CMD_TAG_DISCOVERY) == 0) || (g_strcmp0(str, CMD_TAG_CONNECTION) == 0) || (g_strcmp0(str, CMD_TAG_UI) == 0) || (g_strcmp0(str, CMD_TAG_PLUGINS) == 0));
2800 }
2801 
2802 Command*
cmd_get(const char * const command)2803 cmd_get(const char* const command)
2804 {
2805     if (commands) {
2806         return g_hash_table_lookup(commands, command);
2807     } else {
2808         return NULL;
2809     }
2810 }
2811 
2812 GList*
cmd_get_ordered(const char * const tag)2813 cmd_get_ordered(const char* const tag)
2814 {
2815     GList* ordered_commands = NULL;
2816 
2817     GHashTableIter iter;
2818     gpointer key;
2819     gpointer value;
2820 
2821     g_hash_table_iter_init(&iter, commands);
2822     while (g_hash_table_iter_next(&iter, &key, &value)) {
2823         Command* pcmd = (Command*)value;
2824         if (tag) {
2825             if (_cmd_has_tag(pcmd, tag)) {
2826                 ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
2827             }
2828         } else {
2829             ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
2830         }
2831     }
2832 
2833     return ordered_commands;
2834 }
2835 
2836 static gboolean
_cmd_has_tag(Command * pcmd,const char * const tag)2837 _cmd_has_tag(Command* pcmd, const char* const tag)
2838 {
2839     for (int i = 0; pcmd->help.tags[i] != NULL; i++) {
2840         if (g_strcmp0(tag, pcmd->help.tags[i]) == 0) {
2841             return TRUE;
2842         }
2843     }
2844 
2845     return FALSE;
2846 }
2847 
2848 static int
_cmp_command(Command * cmd1,Command * cmd2)2849 _cmp_command(Command* cmd1, Command* cmd2)
2850 {
2851     return g_strcmp0(cmd1->cmd, cmd2->cmd);
2852 }
2853 
2854 void
command_docgen(void)2855 command_docgen(void)
2856 {
2857     GList* cmds = NULL;
2858 
2859     for (unsigned int i = 0; i < ARRAY_SIZE(command_defs); i++) {
2860         Command* pcmd = command_defs + i;
2861         cmds = g_list_insert_sorted(cmds, pcmd, (GCompareFunc)_cmp_command);
2862     }
2863 
2864     FILE* toc_fragment = fopen("toc_fragment.html", "w");
2865     FILE* main_fragment = fopen("main_fragment.html", "w");
2866 
2867     fputs("<ul><li><ul><li>\n", toc_fragment);
2868     fputs("<hr>\n", main_fragment);
2869 
2870     GList* curr = cmds;
2871     while (curr) {
2872         Command* pcmd = curr->data;
2873 
2874         fprintf(toc_fragment, "<a href=\"#%s\">%s</a>,\n", &pcmd->cmd[1], pcmd->cmd);
2875         fprintf(main_fragment, "<a name=\"%s\"></a>\n", &pcmd->cmd[1]);
2876         fprintf(main_fragment, "<h4>%s</h4>\n", pcmd->cmd);
2877 
2878         fputs("<p><b>Synopsis</b></p>\n", main_fragment);
2879         fputs("<p><pre><code>", main_fragment);
2880         int i = 0;
2881         while (pcmd->help.synopsis[i]) {
2882             char* str1 = str_replace(pcmd->help.synopsis[i], "<", "&lt;");
2883             char* str2 = str_replace(str1, ">", "&gt;");
2884             fprintf(main_fragment, "%s\n", str2);
2885             i++;
2886         }
2887         fputs("</code></pre></p>\n", main_fragment);
2888 
2889         fputs("<p><b>Description</b></p>\n", main_fragment);
2890         fputs("<p>", main_fragment);
2891         fprintf(main_fragment, "%s\n", pcmd->help.desc);
2892         fputs("</p>\n", main_fragment);
2893 
2894         if (pcmd->help.args[0][0] != NULL) {
2895             fputs("<p><b>Arguments</b></p>\n", main_fragment);
2896             fputs("<table>", main_fragment);
2897             for (i = 0; pcmd->help.args[i][0] != NULL; i++) {
2898                 fputs("<tr>", main_fragment);
2899                 fputs("<td>", main_fragment);
2900                 fputs("<code>", main_fragment);
2901                 char* str1 = str_replace(pcmd->help.args[i][0], "<", "&lt;");
2902                 char* str2 = str_replace(str1, ">", "&gt;");
2903                 fprintf(main_fragment, "%s", str2);
2904                 fputs("</code>", main_fragment);
2905                 fputs("</td>", main_fragment);
2906                 fputs("<td>", main_fragment);
2907                 fprintf(main_fragment, "%s", pcmd->help.args[i][1]);
2908                 fputs("</td>", main_fragment);
2909                 fputs("</tr>", main_fragment);
2910             }
2911             fputs("</table>\n", main_fragment);
2912         }
2913 
2914         if (pcmd->help.examples[0] != NULL) {
2915             fputs("<p><b>Examples</b></p>\n", main_fragment);
2916             fputs("<p><pre><code>", main_fragment);
2917             int i = 0;
2918             while (pcmd->help.examples[i]) {
2919                 fprintf(main_fragment, "%s\n", pcmd->help.examples[i]);
2920                 i++;
2921             }
2922             fputs("</code></pre></p>\n", main_fragment);
2923         }
2924 
2925         fputs("<a href=\"#top\"><h5>back to top</h5></a><br><hr>\n", main_fragment);
2926         fputs("\n", main_fragment);
2927 
2928         curr = g_list_next(curr);
2929     }
2930 
2931     fputs("</ul></ul>\n", toc_fragment);
2932 
2933     fclose(toc_fragment);
2934     fclose(main_fragment);
2935     printf("\nProcessed %d commands.\n\n", g_list_length(cmds));
2936     g_list_free(cmds);
2937 }
2938 
2939 void
command_mangen(void)2940 command_mangen(void)
2941 {
2942     GList* cmds = NULL;
2943 
2944     for (unsigned int i = 0; i < ARRAY_SIZE(command_defs); i++) {
2945         Command* pcmd = command_defs + i;
2946         cmds = g_list_insert_sorted(cmds, pcmd, (GCompareFunc)_cmp_command);
2947     }
2948 
2949     mkdir_recursive("docs");
2950 
2951     GDateTime* now = g_date_time_new_now_local();
2952     gchar* date = g_date_time_format(now, "%F");
2953     gchar* header = g_strdup_printf(".TH man 1 \"%s\" \"" PACKAGE_VERSION "\" \"Profanity XMPP client\"\n", date);
2954     if (!header) {
2955         log_error("command_mangen(): could not allocate memory");
2956         return;
2957     }
2958     g_date_time_unref(now);
2959     g_free(date);
2960 
2961     GList* curr = cmds;
2962     while (curr) {
2963         Command* pcmd = curr->data;
2964 
2965         gchar* filename = g_strdup_printf("docs/profanity-%s.1", &pcmd->cmd[1]);
2966         if (!filename) {
2967             log_error("command_mangen(): could not allocate memory");
2968             return;
2969         }
2970         FILE* manpage = fopen(filename, "w");
2971         free(filename);
2972 
2973         fprintf(manpage, "%s\n", header);
2974         fputs(".SH NAME\n", manpage);
2975         fprintf(manpage, "%s\n", pcmd->cmd);
2976 
2977         fputs("\n.SH DESCRIPTION\n", manpage);
2978         fprintf(manpage, "%s\n", pcmd->help.desc);
2979 
2980         fputs("\n.SH SYNOPSIS\n", manpage);
2981         int i = 0;
2982         while (pcmd->help.synopsis[i]) {
2983             fprintf(manpage, "%s\n", pcmd->help.synopsis[i]);
2984             fputs("\n.LP\n", manpage);
2985             i++;
2986         }
2987 
2988         if (pcmd->help.args[0][0] != NULL) {
2989             fputs("\n.SH ARGUMENTS\n", manpage);
2990             for (i = 0; pcmd->help.args[i][0] != NULL; i++) {
2991                 fprintf(manpage, ".PP\n\\fB%s\\fR\n", pcmd->help.args[i][0]);
2992                 fprintf(manpage, ".RS 4\n%s\n.RE\n", pcmd->help.args[i][1]);
2993             }
2994         }
2995 
2996         if (pcmd->help.examples[0] != NULL) {
2997             fputs("\n.SH EXAMPLES\n", manpage);
2998             int i = 0;
2999             while (pcmd->help.examples[i]) {
3000                 fprintf(manpage, "%s\n", pcmd->help.examples[i]);
3001                 fputs("\n.LP\n", manpage);
3002                 i++;
3003             }
3004         }
3005 
3006         fclose(manpage);
3007         curr = g_list_next(curr);
3008     }
3009 
3010     printf("\nProcessed %d commands.\n\n", g_list_length(cmds));
3011 
3012     g_free(header);
3013     g_list_free(cmds);
3014 }
3015