1 /*
2  * commands.c   -- user commands handling
3  *
4  * Copyright (C) 2005-2014 Mikael Berthe <mikael@lilotux.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <string.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <glob.h>
27 
28 #include "config.h"
29 #include "commands.h"
30 #include "help.h"
31 #include "roster.h"
32 #include "screen.h"
33 #include "compl.h"
34 #include "hooks.h"
35 #include "hbuf.h"
36 #include "utils.h"
37 #include "settings.h"
38 #include "events.h"
39 #include "otr.h"
40 #include "carbons.h"
41 #include "utf8.h"
42 #include "xmpp.h"
43 #include "main.h"
44 
45 #define IMSTATUS_AWAY           "away"
46 #define IMSTATUS_ONLINE         "online"
47 #define IMSTATUS_OFFLINE        "offline"
48 #define IMSTATUS_FREE4CHAT      "free"
49 #define IMSTATUS_AVAILABLE      "avail"
50 #define IMSTATUS_NOTAVAILABLE   "notavail"
51 #define IMSTATUS_DONOTDISTURB   "dnd"
52 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
53 # define IMSTATUS_INVISIBLE      "invisible"
54 #endif
55 
56 // Commands callbacks
57 static void do_roster(char *arg);
58 static void do_status(char *arg);
59 static void do_status_to(char *arg);
60 static void do_add(char *arg);
61 static void do_del(char *arg);
62 static void do_group(char *arg);
63 static void do_say(char *arg);
64 static void do_msay(char *arg);
65 static void do_say_to(char *arg);
66 static void do_buffer(char *arg);
67 static void do_clear(char *arg);
68 static void do_info(char *arg);
69 static void do_rename(char *arg);
70 static void do_move(char *arg);
71 static void do_set(char *arg);
72 static void do_alias(char *arg);
73 static void do_bind(char *arg);
74 static void do_connect(char *arg);
75 static void do_disconnect(char *arg);
76 static void do_quit(char *arg);
77 static void do_rawxml(char *arg);
78 static void do_room(char *arg);
79 static void do_authorization(char *arg);
80 static void do_version(char *arg);
81 static void do_request(char *arg);
82 static void do_event(char *arg);
83 static void do_help(char *arg);
84 static void do_pgp(char *arg);
85 static void do_iline(char *arg);
86 static void do_screen_refresh(char *arg);
87 static void do_chat_disable(char *arg);
88 static void do_source(char *arg);
89 static void do_color(char *arg);
90 static void do_otr(char *arg);
91 static void do_otrpolicy(char *arg);
92 static void do_echo(char *arg);
93 static void do_module(char *arg);
94 static void do_carbons(char *arg);
95 
96 static void room_bookmark(gpointer bud, char *arg);
97 
98 // Global variable for the commands list
99 static GSList *Commands;
100 static GSList *safe_commands;
101 
102 #ifdef MODULES_ENABLE
103 #include "modules.h"
104 
cmd_del(gpointer id)105 gpointer cmd_del(gpointer id)
106 {
107   GSList *sl_cmd;
108   if (!id) return NULL;
109   for (sl_cmd = Commands; sl_cmd; sl_cmd = sl_cmd->next)
110     if (sl_cmd -> data == id) {
111       cmd *command = (cmd *) sl_cmd->data;
112       gpointer userdata = command->userdata;
113       Commands = g_slist_delete_link(Commands, sl_cmd);
114       compl_del_category_word(COMPL_CMD, command->name);
115       g_free(command);
116       return userdata;
117     }
118   return NULL;
119 }
120 #endif
121 
122 //  cmd_add()
123 // Adds a command to the commands list and to the CMD completion list
cmd_add(const char * name,const char * help,guint flags_row1,guint flags_row2,void (* f)(char *),gpointer userdata)124 gpointer cmd_add(const char *name, const char *help, guint flags_row1,
125                  guint flags_row2, void (*f)(char*), gpointer userdata)
126 {
127   cmd *n_cmd = g_new0(cmd, 1);
128   strncpy(n_cmd->name, name, 32-1);
129   n_cmd->help = help;
130   n_cmd->completion_flags[0] = flags_row1;
131   n_cmd->completion_flags[1] = flags_row2;
132   n_cmd->func = f;
133   n_cmd->userdata = userdata;
134   Commands = g_slist_prepend(Commands, n_cmd);
135   // Add to completion CMD category
136   compl_add_category_word(COMPL_CMD, name);
137   return n_cmd;
138 }
139 
140 //  cmd_set_safe(name, safe)
141 // Sets if command can be used in startup configuration file.
cmd_set_safe(const gchar * name,gboolean safe)142 gboolean cmd_set_safe(const gchar *name, gboolean safe)
143 {
144   GSList *sel;
145   if (!name)
146     return FALSE;
147   for (sel = safe_commands; sel; sel = sel->next)
148     if (!strcmp((const char *)sel->data, name)) {
149       if (safe) {
150         return FALSE;
151       } else {
152         g_free(sel->data);
153         safe_commands = g_slist_delete_link(safe_commands, sel);
154       }
155     }
156   if (safe)
157     safe_commands = g_slist_append(safe_commands, g_strdup(name));
158   else
159     return FALSE;
160   return TRUE;
161 }
162 
163 //  cmd_is_safe(name)
164 // Returns if command is safe or not
cmd_is_safe(const gchar * name)165 gboolean cmd_is_safe(const gchar *name)
166 {
167   GSList *sel;
168   if (!name)
169     return FALSE;
170   for (sel = safe_commands; sel; sel = sel->next)
171     if (!strcmp((const char *)sel->data, name))
172       return TRUE;
173   return FALSE;
174 }
175 
176 //  cmd_init()
177 // Commands table initialization
178 // !!!
179 // After changing commands names and it arguments names here, you must change
180 // ones in init_bindings()!
181 //
cmd_init(void)182 void cmd_init(void)
183 {
184   cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add, NULL);
185   cmd_add("alias", "Add an alias", 0, 0, &do_alias, NULL);
186   cmd_add("authorization", "Manage subscription authorizations",
187           COMPL_AUTH, COMPL_JID, &do_authorization, NULL);
188   cmd_add("bind", "Add an key binding", 0, 0, &do_bind, NULL);
189   cmd_add("buffer", "Manipulate current buddy's buffer (chat window)",
190           COMPL_BUFFER, 0, &do_buffer, NULL);
191   cmd_add("carbons", "Manage carbons settings", COMPL_CARBONS, 0,
192           &do_carbons, NULL);
193   cmd_add("chat_disable", "Disable chat mode", 0, 0, &do_chat_disable, NULL);
194   cmd_add("clear", "Clear the dialog window", 0, 0, &do_clear, NULL);
195   cmd_add("color", "Set coloring options", COMPL_COLOR, 0, &do_color, NULL);
196   cmd_add("connect", "Connect to the server", 0, 0, &do_connect, NULL);
197   cmd_add("del", "Delete the current buddy", 0, 0, &do_del, NULL);
198   cmd_add("disconnect", "Disconnect from server", 0, 0, &do_disconnect, NULL);
199   cmd_add("echo", "Display a string in the log window", 0, 0, &do_echo, NULL);
200   cmd_add("event", "Process an event", COMPL_EVENTSID, COMPL_EVENTS, &do_event,
201           NULL);
202   cmd_add("group", "Change group display settings",
203           COMPL_GROUP, COMPL_GROUPNAME, &do_group, NULL);
204   cmd_add("help", "Display some help", COMPL_CMD, 0, &do_help, NULL);
205   cmd_add("iline", "Manipulate input buffer", 0, 0, &do_iline, NULL);
206   cmd_add("info", "Show basic info on current buddy", 0, 0, &do_info, NULL);
207   cmd_add("module", "Manipulations with modules", COMPL_MODULE, 0, &do_module,
208           NULL);
209   cmd_add("move", "Move the current buddy to another group", COMPL_GROUPNAME,
210           0, &do_move, NULL);
211   cmd_add("msay", "Send a multi-lines message to the selected buddy",
212           COMPL_MULTILINE, 0, &do_msay, NULL);
213   cmd_add("otr", "Manage OTR settings", COMPL_OTR, COMPL_JID, &do_otr, NULL);
214   cmd_add("otrpolicy", "Manage OTR policies", COMPL_JID, COMPL_OTRPOLICY,
215           &do_otrpolicy, NULL);
216   cmd_add("pgp", "Manage PGP settings", COMPL_PGP, COMPL_JID, &do_pgp, NULL);
217   cmd_add("quit", "Exit the software", 0, 0, &do_quit, NULL);
218   cmd_add("rawxml", "Send a raw XML string", 0, 0, &do_rawxml, NULL);
219   cmd_add("rename", "Rename the current buddy", 0, 0, &do_rename, NULL);
220   cmd_add("request", "Send a Jabber IQ request", COMPL_REQUEST, COMPL_JID,
221           &do_request, NULL);
222   cmd_add("room", "MUC actions command", COMPL_ROOM, 0, &do_room, NULL);
223   cmd_add("roster", "Manipulate the roster/buddylist", COMPL_ROSTER, 0,
224           &do_roster, NULL);
225   cmd_add("say", "Say something to the selected buddy", 0, 0, &do_say, NULL);
226   cmd_add("say_to", "Say something to a specific buddy", COMPL_JID, 0,
227           &do_say_to, NULL);
228   cmd_add("screen_refresh", "Redraw mcabber screen", 0, 0, &do_screen_refresh,
229           NULL);
230   cmd_add("set", "Set/query an option value", 0, 0, &do_set, NULL);
231   cmd_add("source", "Read a configuration file", 0, 0, &do_source, NULL);
232   cmd_add("status", "Show or set your status", COMPL_STATUS, 0, &do_status,
233           NULL);
234   cmd_add("status_to", "Show or set your status for one recipient",
235           COMPL_JID, COMPL_STATUS, &do_status_to, NULL);
236   cmd_add("version", "Show mcabber version", 0, 0, &do_version, NULL);
237 
238   cmd_set_safe("set", TRUE);
239   cmd_set_safe("bind", TRUE);
240   cmd_set_safe("alias", TRUE);
241   cmd_set_safe("pgp", TRUE);
242   cmd_set_safe("source", TRUE);
243   cmd_set_safe("status", TRUE);
244   cmd_set_safe("color", TRUE);
245   cmd_set_safe("otrpolicy", TRUE);
246   cmd_set_safe("module", TRUE);
247 
248   // Status category
249   compl_add_category_word(COMPL_STATUS, "online");
250   compl_add_category_word(COMPL_STATUS, "avail");
251 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
252   compl_add_category_word(COMPL_STATUS, "invisible");
253 #endif
254   compl_add_category_word(COMPL_STATUS, "free");
255   compl_add_category_word(COMPL_STATUS, "dnd");
256   compl_add_category_word(COMPL_STATUS, "notavail");
257   compl_add_category_word(COMPL_STATUS, "away");
258   compl_add_category_word(COMPL_STATUS, "offline");
259   compl_add_category_word(COMPL_STATUS, "message");
260 
261   // Roster category
262   compl_add_category_word(COMPL_ROSTER, "bottom");
263   compl_add_category_word(COMPL_ROSTER, "top");
264   compl_add_category_word(COMPL_ROSTER, "up");
265   compl_add_category_word(COMPL_ROSTER, "down");
266   compl_add_category_word(COMPL_ROSTER, "group_prev");
267   compl_add_category_word(COMPL_ROSTER, "group_next");
268   compl_add_category_word(COMPL_ROSTER, "hide");
269   compl_add_category_word(COMPL_ROSTER, "show");
270   compl_add_category_word(COMPL_ROSTER, "toggle");
271   compl_add_category_word(COMPL_ROSTER, "display");
272   compl_add_category_word(COMPL_ROSTER, "hide_offline");
273   compl_add_category_word(COMPL_ROSTER, "show_offline");
274   compl_add_category_word(COMPL_ROSTER, "toggle_offline");
275   compl_add_category_word(COMPL_ROSTER, "item_lock");
276   compl_add_category_word(COMPL_ROSTER, "item_unlock");
277   compl_add_category_word(COMPL_ROSTER, "item_toggle_lock");
278   compl_add_category_word(COMPL_ROSTER, "alternate");
279   compl_add_category_word(COMPL_ROSTER, "search");
280   compl_add_category_word(COMPL_ROSTER, "unread_first");
281   compl_add_category_word(COMPL_ROSTER, "unread_next");
282   compl_add_category_word(COMPL_ROSTER, "note");
283   compl_add_category_word(COMPL_ROSTER, "resource_lock");
284   compl_add_category_word(COMPL_ROSTER, "resource_unlock");
285 
286   // Buffer category
287   compl_add_category_word(COMPL_BUFFER, "clear");
288   compl_add_category_word(COMPL_BUFFER, "bottom");
289   compl_add_category_word(COMPL_BUFFER, "top");
290   compl_add_category_word(COMPL_BUFFER, "up");
291   compl_add_category_word(COMPL_BUFFER, "down");
292   compl_add_category_word(COMPL_BUFFER, "search_backward");
293   compl_add_category_word(COMPL_BUFFER, "search_forward");
294   compl_add_category_word(COMPL_BUFFER, "readmark");
295   compl_add_category_word(COMPL_BUFFER, "date");
296   compl_add_category_word(COMPL_BUFFER, "%");
297   compl_add_category_word(COMPL_BUFFER, "purge");
298   compl_add_category_word(COMPL_BUFFER, "close");
299   compl_add_category_word(COMPL_BUFFER, "close_all");
300   compl_add_category_word(COMPL_BUFFER, "scroll_lock");
301   compl_add_category_word(COMPL_BUFFER, "scroll_unlock");
302   compl_add_category_word(COMPL_BUFFER, "scroll_toggle");
303   compl_add_category_word(COMPL_BUFFER, "list");
304   compl_add_category_word(COMPL_BUFFER, "save");
305 
306   // Group category
307   compl_add_category_word(COMPL_GROUP, "fold");
308   compl_add_category_word(COMPL_GROUP, "unfold");
309   compl_add_category_word(COMPL_GROUP, "toggle");
310 
311   // Multi-line (msay) category
312   compl_add_category_word(COMPL_MULTILINE, "abort");
313   compl_add_category_word(COMPL_MULTILINE, "begin");
314   compl_add_category_word(COMPL_MULTILINE, "send");
315   compl_add_category_word(COMPL_MULTILINE, "send_to");
316   compl_add_category_word(COMPL_MULTILINE, "toggle");
317   compl_add_category_word(COMPL_MULTILINE, "toggle_verbatim");
318   compl_add_category_word(COMPL_MULTILINE, "verbatim");
319 
320   // Room category
321   compl_add_category_word(COMPL_ROOM, "affil");
322   compl_add_category_word(COMPL_ROOM, "ban");
323   compl_add_category_word(COMPL_ROOM, "bookmark");
324   compl_add_category_word(COMPL_ROOM, "destroy");
325   compl_add_category_word(COMPL_ROOM, "invite");
326   compl_add_category_word(COMPL_ROOM, "join");
327   compl_add_category_word(COMPL_ROOM, "kick");
328   compl_add_category_word(COMPL_ROOM, "leave");
329   compl_add_category_word(COMPL_ROOM, "names");
330   compl_add_category_word(COMPL_ROOM, "nick");
331   compl_add_category_word(COMPL_ROOM, "privmsg");
332   compl_add_category_word(COMPL_ROOM, "remove");
333   compl_add_category_word(COMPL_ROOM, "role");
334   compl_add_category_word(COMPL_ROOM, "setopt");
335   compl_add_category_word(COMPL_ROOM, "topic");
336   compl_add_category_word(COMPL_ROOM, "unban");
337   compl_add_category_word(COMPL_ROOM, "unlock");
338   compl_add_category_word(COMPL_ROOM, "whois");
339 
340   // Authorization category
341   compl_add_category_word(COMPL_AUTH, "allow");
342   compl_add_category_word(COMPL_AUTH, "cancel");
343   compl_add_category_word(COMPL_AUTH, "request");
344   compl_add_category_word(COMPL_AUTH, "request_unsubscribe");
345 
346   // Request (query) category
347   compl_add_category_word(COMPL_REQUEST, "last");
348   compl_add_category_word(COMPL_REQUEST, "ping");
349   compl_add_category_word(COMPL_REQUEST, "time");
350   compl_add_category_word(COMPL_REQUEST, "vcard");
351   compl_add_category_word(COMPL_REQUEST, "version");
352 
353   // Events category
354   compl_add_category_word(COMPL_EVENTS, "accept");
355   compl_add_category_word(COMPL_EVENTS, "ignore");
356   compl_add_category_word(COMPL_EVENTS, "reject");
357 
358   // PGP category
359   compl_add_category_word(COMPL_PGP, "disable");
360   compl_add_category_word(COMPL_PGP, "enable");
361   compl_add_category_word(COMPL_PGP, "force");
362   compl_add_category_word(COMPL_PGP, "info");
363   compl_add_category_word(COMPL_PGP, "setkey");
364 
365   // OTR category
366   compl_add_category_word(COMPL_OTR, "start");
367   compl_add_category_word(COMPL_OTR, "stop");
368   compl_add_category_word(COMPL_OTR, "fingerprint");
369   compl_add_category_word(COMPL_OTR, "smpq");
370   compl_add_category_word(COMPL_OTR, "smpr");
371   compl_add_category_word(COMPL_OTR, "smpa");
372   compl_add_category_word(COMPL_OTR, "info");
373   compl_add_category_word(COMPL_OTR, "key");
374 
375   // OTR Policy category
376   compl_add_category_word(COMPL_OTRPOLICY, "plain");
377   compl_add_category_word(COMPL_OTRPOLICY, "manual");
378   compl_add_category_word(COMPL_OTRPOLICY, "opportunistic");
379   compl_add_category_word(COMPL_OTRPOLICY, "always");
380 
381   // Color category
382   compl_add_category_word(COMPL_COLOR, "roster");
383   compl_add_category_word(COMPL_COLOR, "muc");
384   compl_add_category_word(COMPL_COLOR, "mucnick");
385 
386 #ifdef MODULES_ENABLE
387   // Module category
388   compl_add_category_word(COMPL_MODULE, "info");
389   compl_add_category_word(COMPL_MODULE, "list");
390   compl_add_category_word(COMPL_MODULE, "load");
391   compl_add_category_word(COMPL_MODULE, "unload");
392 #endif
393 
394   // Carbons category
395   compl_add_category_word(COMPL_CARBONS, "info");
396   compl_add_category_word(COMPL_CARBONS, "enable");
397   compl_add_category_word(COMPL_CARBONS, "disable");
398 }
399 
400 //  expandalias(line)
401 // If there is one, expand the alias in line and returns a new allocated line
402 // If no alias is found, returns line
403 // Note: if the returned pointer is different from line, the caller should
404 //       g_free() the pointer after use
expandalias(const char * line)405 char *expandalias(const char *line)
406 {
407   const char *p1, *p2;
408   char *word;
409   const gchar *value;
410   char *newline = (char*)line;
411 
412   // Ignore leading COMMAND_CHAR
413   for (p1 = line ; *p1 == COMMAND_CHAR ; p1++)
414     ;
415   // Locate the end of the word
416   for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
417     ;
418   // Extract the word and look for an alias in the list
419   word = g_strndup(p1, p2-p1);
420   value = settings_get(SETTINGS_TYPE_ALIAS, (const char*)word);
421   g_free(word);
422 
423   if (value)
424     newline = g_strdup_printf("%c%s%s", COMMAND_CHAR, value, p2);
425 
426   return newline;
427 }
428 
429 //  cmd_get
430 // Finds command in the command list structure.
431 // Returns a pointer to the cmd entry, or NULL if command not found.
cmd_get(const char * command)432 cmd *cmd_get(const char *command)
433 {
434   const char *p1, *p2;
435   char *com;
436   GSList *sl_com;
437 
438   // Ignore leading COMMAND_CHAR
439   for (p1 = command ; *p1 == COMMAND_CHAR ; p1++)
440     ;
441   // Locate the end of the command
442   for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++)
443     ;
444   // Copy the clean command
445   com = g_strndup(p1, p2-p1);
446 
447   // Look for command in the list
448   for (sl_com=Commands; sl_com; sl_com = g_slist_next(sl_com)) {
449     if (!strcasecmp(com, ((cmd*)sl_com->data)->name))
450       break;
451   }
452   g_free(com);
453 
454   if (sl_com)       // Command has been found.
455     return (cmd*)sl_com->data;
456   return NULL;
457 }
458 
459 //  process_command(line, iscmd)
460 // Process a command line.
461 // If iscmd is TRUE, process the command even if verbatim mmode is set;
462 // it is intended to be used for key bindings.
process_command(const char * line,guint iscmd)463 void process_command(const char *line, guint iscmd)
464 {
465   char *p;
466   char *xpline;
467   cmd *curcmd;
468 
469   if (!line)
470     return;
471 
472   // We do alias expansion here
473   if (iscmd || scr_get_multimode() != 2)
474     xpline = expandalias(line);
475   else
476     xpline = (char*)line; // No expansion in verbatim multi-line mode
477 
478   // We want to use a copy
479   if (xpline == line)
480     xpline = g_strdup(line);
481 
482   // Remove trailing spaces:
483   for (p=xpline ; *p ; p++)
484     ;
485   for (p-- ; p>xpline && (*p == ' ') ; p--)
486     *p = 0;
487 
488   // If verbatim multi-line mode, we check if another /msay command is typed
489   if (!iscmd && scr_get_multimode() == 2
490       && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
491     // It isn't an /msay command
492     scr_append_multiline(xpline);
493     g_free(xpline);
494     return;
495   }
496 
497   // Commands handling
498   curcmd = cmd_get(xpline);
499 
500   if (!curcmd) {
501     scr_LogPrint(LPRINT_NORMAL, "Unrecognized command.  "
502                  "Please see the manual for a list of known commands.");
503     g_free(xpline);
504     return;
505   }
506   if (!curcmd->func) {
507     scr_LogPrint(LPRINT_NORMAL,
508                  "This functionality is not yet implemented, sorry.");
509     g_free(xpline);
510     return;
511   }
512   // Lets go to the command parameters
513   for (p = xpline+1; *p && (*p != ' ') ; p++)
514     ;
515   // Skip spaces
516   while (*p && (*p == ' '))
517     p++;
518   // Call command-specific function
519 #ifdef MODULES_ENABLE
520   if (curcmd->userdata)
521     (*(void (*)(char *p, gpointer u))curcmd->func)(p, curcmd->userdata);
522   else
523     (*curcmd->func)(p);
524 #else
525   (*curcmd->func)(p);
526 #endif
527   g_free(xpline);
528 }
529 
530 //  process_line(line)
531 // Process a command/message line.
532 // If this isn't a command, this is a message and it is sent to the
533 // currently selected buddy.
process_line(const char * line)534 void process_line(const char *line)
535 {
536   if (!*line) { // User only pressed enter
537     if (scr_get_multimode()) {
538       scr_append_multiline("");
539       return;
540     }
541     if (current_buddy) {
542       if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP) {
543         do_group("toggle");
544       } else {
545         // Enter chat mode
546         scr_set_chatmode(TRUE);
547         scr_show_buddy_window();
548       }
549     }
550     return;
551   }
552 
553   if (*line == COMMAND_CHAR) {
554     if (*(line+1) != COMMAND_CHAR) {
555       /* It is a command */
556       process_command(line, FALSE);
557       return;
558     } else if (scr_get_multimode() != 2) {
559       /* Skip the first COMMAND_CHAR */
560       line++;
561     }
562   }
563 
564   // This isn't a command
565   if (scr_get_multimode())
566     scr_append_multiline(line);
567   else
568     say_cmd((char*)line, 0);
569 }
570 
571 // Helper routine for buffer item_{lock,unlock,toggle_lock}
572 // "lock" values: 1=lock 0=unlock -1=invert
roster_buddylock(char * bjid,int lock)573 static void roster_buddylock(char *bjid, int lock)
574 {
575   gpointer bud = NULL;
576 
577   // Allow special jid "" or "." (current buddy)
578   if (bjid && (!*bjid || !strcmp(bjid, ".")))
579     bjid = NULL;
580 
581   if (bjid) {
582     // The JID has been specified.  Quick check...
583     if (check_jid_syntax(bjid)) {
584       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
585                    "<%s> is not a valid Jabber ID.", bjid);
586     } else {
587       // Find the buddy
588       GSList *roster_elt;
589       roster_elt = roster_find(bjid, jidsearch,
590                                ROSTER_TYPE_USER|ROSTER_TYPE_ROOM);
591       if (roster_elt)
592         bud = roster_elt->data;
593       else
594         scr_LogPrint(LPRINT_NORMAL, "This jid isn't in the roster.");
595     }
596   } else {
597     // Use the current buddy
598     if (current_buddy)
599       bud = BUDDATA(current_buddy);
600   }
601 
602   // Update the ROSTER_FLAG_USRLOCK flag
603   if (bud) {
604     if (lock == -1)
605       lock = !(buddy_getflags(bud) & ROSTER_FLAG_USRLOCK);
606     buddy_setflags(bud, ROSTER_FLAG_USRLOCK, lock);
607     buddylist_defer_build();
608     scr_update_roster();
609   }
610 }
611 
roster_resourcelock(char * jidres,gboolean lock)612 static void roster_resourcelock(char *jidres, gboolean lock) {
613   gpointer bud = NULL;
614   char *resource = NULL;
615 
616   if (!jidres) {
617     if (lock) return;
618     jidres = ".";
619   }
620 
621   if (jidres[0] == '.' &&
622       (jidres[1] == '\0' || jidres[1] == JID_RESOURCE_SEPARATOR)) {
623     //Special jid: . or ./resource
624     if (current_buddy)
625       bud = BUDDATA(current_buddy);
626     if (jidres[1] == JID_RESOURCE_SEPARATOR)
627       resource = jidres+2;
628   } else {
629     if (!check_jid_syntax(jidres) &&
630         jid_get_resource_name(jidres)) {
631       //Any other valid full jid
632       char * bare_jid = jidtodisp(jidres);
633       GSList *roster_elt;
634       roster_elt = roster_find(bare_jid, jidsearch,
635           ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
636       if (roster_elt)
637         bud = roster_elt->data;
638       g_free(bare_jid);
639     }
640     if (!bud) {
641       //Resource for current buddy
642       if (current_buddy)
643         bud = BUDDATA(current_buddy);
644       resource = jidres;
645     }
646   }
647 
648   if (bud && buddy_gettype(bud) & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
649     if (lock) {
650       GSList *resources, *p_res;
651       gboolean found = FALSE;
652       resources = buddy_getresources(bud);
653       for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
654         if (!g_strcmp0((char*)p_res->data, resource))
655           found = TRUE;
656         g_free(p_res->data);
657       }
658       g_slist_free(resources);
659       if (!found) {
660         scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
661         return;
662       }
663     } else {
664       resource = NULL;
665     }
666     buddy_setactiveresource(bud, resource);
667     scr_update_chat_status(TRUE);
668   }
669 }
670 //  display_and_free_note(note, winId)
671 // Display the note information in the winId buffer, and free note
672 // (winId is a bare jid or NULL for the status window, in which case we
673 // display the note jid too)
display_and_free_note(struct annotation * note,const char * winId)674 static void display_and_free_note(struct annotation *note, const char *winId)
675 {
676   gchar tbuf[128];
677   GString *sbuf;
678   guint msg_flag = HBB_PREFIX_INFO;
679   /* We use the flag prefix_info for the first line, and prefix_cont
680      for the other lines, for better readability */
681 
682   if (!note)
683     return;
684 
685   sbuf = g_string_new("");
686 
687   if (!winId) {
688     // We're writing to the status window, so let's show the jid too.
689     g_string_printf(sbuf, "Annotation on <%s>", note->jid);
690     scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
691     msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
692   }
693 
694   // If we have the creation date, display it
695   if (note->cdate) {
696     strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
697              localtime(&note->cdate));
698     g_string_printf(sbuf, "Note created  %s", tbuf);
699     scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
700     msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
701   }
702   // If we have the modification date, display it
703   // unless it's the same as the creation date
704   if (note->mdate && note->mdate != note->cdate) {
705     strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S",
706              localtime(&note->mdate));
707     g_string_printf(sbuf, "Note modified %s", tbuf);
708     scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
709     msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT;
710   }
711   // Note text
712   g_string_printf(sbuf, "Note: %s", note->text);
713   scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0);
714 
715   g_string_free(sbuf, TRUE);
716   g_free(note->text);
717   g_free(note->jid);
718   g_free(note);
719 }
720 
display_all_annotations(void)721 static void display_all_annotations(void)
722 {
723   GSList *notes;
724   notes = xmpp_get_all_storage_rosternotes();
725 
726   if (!notes)
727     return;
728 
729   // Call display_and_free_note() for each note,
730   // with winId = NULL (special window)
731   g_slist_foreach(notes, (GFunc)&display_and_free_note, NULL);
732   scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
733   scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
734                                  ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
735   g_slist_free(notes);
736 }
737 
roster_note(char * arg)738 static void roster_note(char *arg)
739 {
740   const char *bjid;
741   guint type;
742 
743   if (!current_buddy)
744     return;
745 
746   bjid = buddy_getjid(BUDDATA(current_buddy));
747   type = buddy_gettype(BUDDATA(current_buddy));
748 
749   if (!bjid && type == ROSTER_TYPE_SPECIAL && !arg) {
750     // We're in the status window (the only special buffer currently)
751     // Let's display all server notes
752     display_all_annotations();
753     return;
754   }
755 
756   if (!bjid || (type != ROSTER_TYPE_USER &&
757                type != ROSTER_TYPE_ROOM &&
758                type != ROSTER_TYPE_AGENT)) {
759     scr_LogPrint(LPRINT_NORMAL, "This item can't have a note.");
760     return;
761   }
762 
763   if (arg && *arg) {  // Set a note
764     gchar *msg, *notetxt;
765     msg = to_utf8(arg);
766     if (!strcmp(msg, "-"))
767       notetxt = NULL; // delete note
768     else
769       notetxt = msg;
770     xmpp_set_storage_rosternotes(bjid, notetxt);
771     g_free(msg);
772   } else {      // Display a note
773     struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
774     if (note) {
775       display_and_free_note(note, bjid);
776     } else {
777       scr_WriteIncomingMessage(bjid, "This item doesn't have a note.", 0,
778                                HBB_PREFIX_INFO, 0);
779     }
780   }
781 }
782 
783 //  roster_updown(updown, nitems)
784 // updown: -1=up, +1=down
roster_updown(int updown,char * nitems)785 inline static void roster_updown(int updown, char *nitems)
786 {
787   int nbitems;
788 
789   if (!nitems || !*nitems)
790     nbitems = 1;
791   else
792     nbitems = strtol(nitems, NULL, 10);
793 
794   if (nbitems > 0)
795     scr_roster_up_down(updown, nbitems);
796 }
797 
798 /* Commands callback functions */
799 /* All these do_*() functions will be called with a "arg" parameter */
800 /* (with arg not null)                                              */
801 
do_roster(char * arg)802 static void do_roster(char *arg)
803 {
804   char **paramlst;
805   char *subcmd;
806 
807   paramlst = split_arg(arg, 2, 1); // subcmd, arg
808   subcmd = *paramlst;
809   arg = *(paramlst+1);
810 
811   if (!subcmd || !*subcmd) {
812     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
813     free_arg_lst(paramlst);
814     return;
815   }
816 
817   if (!strcasecmp(subcmd, "top")) {
818     scr_roster_top();
819   } else if (!strcasecmp(subcmd, "bottom")) {
820     scr_roster_bottom();
821   } else if (!strcasecmp(subcmd, "hide")) {
822     scr_roster_visibility(0);
823   } else if (!strcasecmp(subcmd, "show")) {
824     scr_roster_visibility(1);
825   } else if (!strcasecmp(subcmd, "toggle")) {
826     scr_roster_visibility(-1);
827   } else if (!strcasecmp(subcmd, "hide_offline")) {
828     buddylist_set_hide_offline_buddies(TRUE);
829     scr_update_roster();
830   } else if (!strcasecmp(subcmd, "show_offline")) {
831     buddylist_set_hide_offline_buddies(FALSE);
832     scr_update_roster();
833   } else if (!strcasecmp(subcmd, "toggle_offline")) {
834     buddylist_set_hide_offline_buddies(-1);
835     scr_update_roster();
836   } else if (!strcasecmp(subcmd, "display")) {
837     scr_roster_display(arg);
838   } else if (!strcasecmp(subcmd, "item_lock")) {
839     roster_buddylock(arg, 1);
840   } else if (!strcasecmp(subcmd, "item_unlock")) {
841     roster_buddylock(arg, 0);
842   } else if (!strcasecmp(subcmd, "item_toggle_lock")) {
843     roster_buddylock(arg, -1);
844   } else if (!strcasecmp(subcmd, "unread_first")) {
845     scr_roster_unread_message(0);
846   } else if (!strcasecmp(subcmd, "unread_next")) {
847     scr_roster_unread_message(1);
848   } else if (!strcasecmp(subcmd, "next_open_buffer")) {
849     scr_roster_next_open_buffer();
850   } else if (!strcasecmp(subcmd, "alternate")) {
851     scr_roster_jump_alternate();
852   } else if (!strncasecmp(subcmd, "search", 6)) {
853     strip_arg_special_chars(arg);
854     if (!arg || !*arg) {
855       scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
856       free_arg_lst(paramlst);
857       return;
858     }
859     scr_roster_search(arg);
860   } else if (!strcasecmp(subcmd, "up")) {
861     roster_updown(-1, arg);
862   } else if (!strcasecmp(subcmd, "down")) {
863     roster_updown(1, arg);
864   } else if (!strcasecmp(subcmd, "group_prev")) {
865     scr_roster_prev_group();
866   } else if (!strcasecmp(subcmd, "group_next")) {
867     scr_roster_next_group();
868   } else if (!strcasecmp(subcmd, "note")) {
869     roster_note(arg);
870   } else if (!strcasecmp(subcmd, "resource_lock")) {
871     roster_resourcelock(arg, TRUE);
872   } else if (!strcasecmp(subcmd, "resource_unlock")) {
873     roster_resourcelock(arg, FALSE);
874   } else {
875     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
876   }
877   free_arg_lst(paramlst);
878 }
879 
do_color(char * arg)880 void do_color(char *arg)
881 {
882   char **paramlst;
883   char *subcmd;
884 
885   paramlst = split_arg(arg, 2, 1); // subcmd, arg
886   subcmd = *paramlst;
887   arg = *(paramlst+1);
888 
889   if (!subcmd || !*subcmd) {
890     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
891     free_arg_lst(paramlst);
892     return;
893   }
894 
895   if (!strcasecmp(subcmd, "roster")) {
896     char *status, *wildcard, *color;
897     char **arglist = split_arg(arg, 3, 0);
898 
899     status = *arglist;
900     wildcard = to_utf8(arglist[1]);
901     color = arglist[2];
902 
903     if (status && !strcmp(status, "clear")) { // Not a color command, clear all
904       scr_roster_clear_color();
905     } else {
906       if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
907         scr_LogPrint(LPRINT_NORMAL, "Missing argument");
908       } else {
909         scr_roster_color(status, wildcard, color);
910       }
911     }
912     free_arg_lst(arglist);
913     g_free(wildcard);
914   } else if (!strcasecmp(subcmd, "muc")) {
915     char **arglist = split_arg(arg, 2, 0);
916     char *free_muc = to_utf8(*arglist);
917     const char *muc = free_muc, *mode = arglist[1];
918     if (!muc || !*muc) {
919       scr_LogPrint(LPRINT_NORMAL, "What MUC?");
920     } else {
921       if (!strcmp(muc, "."))
922         if (!(muc = CURRENT_JID))
923           scr_LogPrint(LPRINT_NORMAL, "No JID selected");
924       if (muc) {
925         if (check_jid_syntax(muc) && strcmp(muc, "*")) {
926           scr_LogPrint(LPRINT_NORMAL, "Not a JID");
927         } else {
928           if (!mode || !*mode || !strcasecmp(mode, "on"))
929             scr_muc_color(muc, MC_ALL);
930           else if (!strcasecmp(mode, "preset"))
931             scr_muc_color(muc, MC_PRESET);
932           else if (!strcasecmp(mode, "off"))
933             scr_muc_color(muc, MC_OFF);
934           else if (!strcmp(mode, "-"))
935             scr_muc_color(muc, MC_REMOVE);
936           else
937             scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode");
938         }
939       }
940     }
941     free_arg_lst(arglist);
942     g_free(free_muc);
943   } else if (!strcasecmp(subcmd, "mucnick")) {
944     char **arglist = split_arg(arg, 2, 0);
945     const char *nick = *arglist, *color = arglist[1];
946     if (!nick || !*nick || !color || !*color)
947       scr_LogPrint(LPRINT_NORMAL, "Missing argument");
948     else
949       scr_muc_nick_color(nick, color);
950     free_arg_lst(arglist);
951   } else {
952     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
953   }
954   free_arg_lst(paramlst);
955 }
956 
957 //  cmd_setstatus(recipient, arg)
958 // Set your Jabber status.
959 // - if recipient is not NULL, the status is sent to this contact only
960 // - arg must be "status message" (message is optional)
cmd_setstatus(const char * recipient,const char * arg)961 void cmd_setstatus(const char *recipient, const char *arg)
962 {
963   char **paramlst;
964   char *status;
965   char *msg;
966   enum imstatus st;
967 
968   if (!xmpp_is_online())
969     scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
970   // We do not return now, so that the status is memorized and used later...
971 
972   // It makes sense to reset autoaway before changing the status
973   // (esp. for FIFO or remote commands) or the behaviour could be
974   // unexpected...
975   if (!recipient)
976     scr_check_auto_away(TRUE);
977 
978   paramlst = split_arg(arg, 2, 1); // status, message
979   status = *paramlst;
980   msg = *(paramlst+1);
981 
982   if (!status) {
983     free_arg_lst(paramlst);
984     return;
985   }
986 
987   if      (!strcasecmp(status, IMSTATUS_OFFLINE))       st = offline;
988   else if (!strcasecmp(status, IMSTATUS_ONLINE))        st = available;
989   else if (!strcasecmp(status, IMSTATUS_AVAILABLE))     st = available;
990   else if (!strcasecmp(status, IMSTATUS_AWAY))          st = away;
991 #ifdef WITH_DEPRECATED_STATUS_INVISIBLE
992   else if (!strcasecmp(status, IMSTATUS_INVISIBLE))     st = invisible;
993 #endif
994   else if (!strcasecmp(status, IMSTATUS_DONOTDISTURB))  st = dontdisturb;
995   else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
996   else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
997   else if (!strcasecmp(status, "message")) {
998     if (!msg || !*msg) {
999       // We want a message.  If there's none, we give up.
1000       scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1001       free_arg_lst(paramlst);
1002       return;
1003     }
1004     st = xmpp_getstatus();  // Preserve current status
1005   } else {
1006     scr_LogPrint(LPRINT_NORMAL, "Unrecognized status!");
1007     free_arg_lst(paramlst);
1008     return;
1009   }
1010 
1011   // Use provided message
1012   if (msg && !*msg) {
1013     msg = NULL;
1014   }
1015 
1016   // If a recipient is specified, let's don't use default status messages
1017   if (recipient && !msg)
1018     msg = "";
1019 
1020   xmpp_setstatus(st, recipient, msg, FALSE);
1021 
1022   free_arg_lst(paramlst);
1023 }
1024 
do_status(char * arg)1025 static void do_status(char *arg)
1026 {
1027   if (!*arg) {
1028     const char *sm = xmpp_getstatusmsg();
1029     scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
1030                  imstatus2char[xmpp_getstatus()],
1031                  (sm ? sm : ""));
1032     return;
1033   }
1034   arg = to_utf8(arg);
1035   cmd_setstatus(NULL, arg);
1036   g_free(arg);
1037 }
1038 
do_status_to(char * arg)1039 static void do_status_to(char *arg)
1040 {
1041   char **paramlst;
1042   char *fjid, *st, *msg;
1043   char *jid_utf8 = NULL;
1044 
1045   paramlst = split_arg(arg, 3, 1); // jid, status, [message]
1046   fjid = *paramlst;
1047   st = *(paramlst+1);
1048   msg = *(paramlst+2);
1049 
1050   if (!fjid || !st) {
1051     scr_LogPrint(LPRINT_NORMAL,
1052                  "Please specify both a Jabber ID and a status.");
1053     free_arg_lst(paramlst);
1054     return;
1055   }
1056 
1057   // Allow things like /status_to "" away
1058   if (!*fjid || !strcmp(fjid, "."))
1059     fjid = NULL;
1060 
1061   if (fjid) {
1062     // The JID has been specified.  Quick check...
1063     if (check_jid_syntax(fjid)) {
1064       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
1065                    "<%s> is not a valid Jabber ID.", fjid);
1066       fjid = NULL;
1067     } else {
1068       // Convert jid to lowercase
1069       char *p = fjid;
1070       for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
1071         *p = tolower(*p);
1072       fjid = jid_utf8 = to_utf8(fjid);
1073     }
1074   } else {
1075     // Add the current buddy
1076     if (current_buddy)
1077       fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
1078     if (!fjid)
1079       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
1080   }
1081 
1082   if (fjid) {
1083     char *cmdline;
1084     if (!msg)
1085       msg = "";
1086     msg = to_utf8(msg);
1087     cmdline = g_strdup_printf("%s %s", st, msg);
1088     scr_LogPrint(LPRINT_LOGNORM, "Sending to <%s> /status %s", fjid, cmdline);
1089     cmd_setstatus(fjid, cmdline);
1090     g_free(msg);
1091     g_free(cmdline);
1092     g_free(jid_utf8);
1093   }
1094   free_arg_lst(paramlst);
1095 }
1096 
do_add(char * arg)1097 static void do_add(char *arg)
1098 {
1099   char **paramlst;
1100   char *id, *nick;
1101   char *jid_utf8 = NULL;
1102 
1103   if (!xmpp_is_online()) {
1104     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
1105     return;
1106   }
1107 
1108   paramlst = split_arg(arg, 2, 0); // jid, [nickname]
1109   id = *paramlst;
1110   nick = *(paramlst+1);
1111 
1112   if (!id)
1113     nick = NULL; // Allow things like: /add "" nick
1114   else if (!*id || !strcmp(id, "."))
1115     id = NULL;
1116 
1117   if (id) {
1118     // The JID has been specified.  Quick check...
1119     if (check_jid_syntax(id)) {
1120       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
1121                    "<%s> is not a valid Jabber ID.", id);
1122       id = NULL;
1123     } else {
1124       mc_strtolower(id);
1125       id = jid_utf8 = to_utf8(id);
1126     }
1127   } else {
1128     // Add the current buddy
1129     if (current_buddy)
1130       id = (char*)buddy_getjid(BUDDATA(current_buddy));
1131     if (!id)
1132       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
1133   }
1134 
1135   if (nick)
1136     nick = to_utf8(nick);
1137 
1138   if (id) {
1139     // 2nd parameter = optional nickname
1140     xmpp_addbuddy(id, nick, NULL);
1141     scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
1142                  id);
1143   }
1144 
1145   g_free(jid_utf8);
1146   g_free(nick);
1147   free_arg_lst(paramlst);
1148 }
1149 
do_del(char * arg)1150 static void do_del(char *arg)
1151 {
1152   const char *bjid;
1153 
1154   if (*arg) {
1155     scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
1156                  "the currently-selected buddy will be deleted.");
1157     return;
1158   }
1159 
1160   if (!current_buddy)
1161     return;
1162   bjid = buddy_getjid(BUDDATA(current_buddy));
1163   if (!bjid)
1164     return;
1165 
1166   if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
1167     // This is a chatroom
1168     if (buddy_getinsideroom(BUDDATA(current_buddy))) {
1169       scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
1170       return;
1171     }
1172   }
1173 
1174   // Close the buffer
1175   scr_buffer_purge(1, NULL);
1176 
1177   scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
1178   xmpp_delbuddy(bjid);
1179   scr_update_buddy_window();
1180 }
1181 
do_group(char * arg)1182 static void do_group(char *arg)
1183 {
1184   gpointer group = NULL;
1185   guint leave_buddywindow;
1186   char **paramlst;
1187   char *subcmd;
1188   enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
1189 
1190   if (!*arg) {
1191     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1192     return;
1193   }
1194 
1195   if (!current_buddy)
1196     return;
1197 
1198   paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
1199   subcmd = *paramlst;
1200   arg = *(paramlst+1);
1201 
1202   if (!subcmd || !*subcmd)
1203     goto do_group_return;   // Should not happen
1204 
1205   if (!strcasecmp(subcmd, "expand") || !strcasecmp(subcmd, "unfold")) {
1206     group_state = group_unfold;
1207   } else if (!strcasecmp(subcmd, "shrink") || !strcasecmp(subcmd, "fold")) {
1208     group_state = group_fold;
1209   } else if (!strcasecmp(subcmd, "toggle")) {
1210     group_state = group_toggle;
1211   } else {
1212     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
1213     goto do_group_return;
1214   }
1215 
1216   if (arg && *arg) {
1217     GSList *roster_elt;
1218     char *group_utf8 = to_utf8(arg);
1219     roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
1220     g_free(group_utf8);
1221     if (roster_elt)
1222       group = buddy_getgroup(roster_elt->data);
1223   } else {
1224     group = buddy_getgroup(BUDDATA(current_buddy));
1225   }
1226   if (!group) {
1227     scr_LogPrint(LPRINT_NORMAL, "Group not found.");
1228     goto do_group_return;
1229   }
1230 
1231   // We'll have to redraw the chat window if we're not currently on the group
1232   // entry itself, because it means we'll have to leave the current buddy
1233   // chat window.
1234   leave_buddywindow = (group != BUDDATA(current_buddy) &&
1235                        group == buddy_getgroup(BUDDATA(current_buddy)));
1236 
1237   if (!(buddy_gettype(group) & ROSTER_TYPE_GROUP)) {
1238     scr_LogPrint(LPRINT_NORMAL, "You need to select a group.");
1239     goto do_group_return;
1240   }
1241 
1242   if (group_state != group_unfold && leave_buddywindow)
1243     scr_roster_prev_group();
1244 
1245   buddy_hide_group(group, group_state);
1246 
1247   buddylist_defer_build();
1248   scr_update_roster();
1249 
1250 do_group_return:
1251   free_arg_lst(paramlst);
1252 }
1253 
send_message_to(const char * fjid,const char * msg,const char * subj,LmMessageSubType type_overwrite,bool quiet)1254 static int send_message_to(const char *fjid, const char *msg, const char *subj,
1255                            LmMessageSubType type_overwrite, bool quiet)
1256 {
1257   char *bare_jid;
1258   const char *muc_nick;
1259   char *hmsg;
1260   gint crypted;
1261   gint retval = 0;
1262   int isroom;
1263   gpointer xep184 = NULL;
1264 
1265   if (!xmpp_is_online()) {
1266     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
1267     return 1;
1268   }
1269   if (!fjid || !*fjid) {
1270     scr_LogPrint(LPRINT_NORMAL, "You must specify a Jabber ID.");
1271     return 1;
1272   }
1273   if (!msg || !*msg) {
1274     scr_LogPrint(LPRINT_NORMAL, "You must specify a message.");
1275     return 1;
1276   }
1277   if (check_jid_syntax((char*)fjid)) {
1278     scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
1279                  "<%s> is not a valid Jabber ID.", fjid);
1280     return 1;
1281   }
1282 
1283   // We must use the bare jid in hk_message_out()
1284   bare_jid = jidtodisp(fjid);
1285 
1286   if (!quiet) {
1287     // Jump to window, create one if needed
1288     scr_roster_jump_jid(bare_jid);
1289   }
1290 
1291   // Check if we're sending a message to a conference room
1292   if (NULL != roster_find(bare_jid, jidsearch, ROSTER_TYPE_ROOM)) {
1293     muc_nick = jid_get_resource_name(fjid);
1294     isroom = !muc_nick; // if a resource is specified, then it's a muc private message, not a room
1295   } else {
1296     isroom = false;
1297     muc_nick = NULL;
1298   }
1299 
1300   // local part (UI, logging, etc.)
1301   if (subj)
1302     hmsg = g_strdup_printf("[%s]\n%s", subj, msg);
1303   else
1304     hmsg = (char*)msg;
1305 
1306   // Network part
1307   xmpp_send_msg(fjid, msg, (isroom ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER),
1308                 subj, FALSE, &crypted, type_overwrite, &xep184);
1309 
1310   if (crypted == -1) {
1311     scr_LogPrint(LPRINT_LOGNORM, "Encryption error.  Message was not sent.");
1312     retval = 1;
1313     goto send_message_to_return;
1314   }
1315 
1316   // Hook
1317   if (!isroom)
1318     hk_message_out(bare_jid, muc_nick, 0, hmsg, crypted, FALSE, xep184);
1319 
1320 send_message_to_return:
1321   if (hmsg != msg) g_free(hmsg);
1322   g_free(bare_jid);
1323   return retval;
1324 }
1325 
1326 //  send_message(msg, subj, type_overwrite)
1327 // Write the message in the buddy's window and send the message on
1328 // the network.
send_message(const char * msg,const char * subj,LmMessageSubType type_overwrite)1329 static void send_message(const char *msg, const char *subj,
1330                          LmMessageSubType type_overwrite)
1331 {
1332   const char *bjid;
1333   char *jid;
1334   const char *activeres;
1335 
1336   if (!current_buddy) {
1337     scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
1338     return;
1339   }
1340 
1341   bjid = CURRENT_JID;
1342   if (!bjid) {
1343     scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
1344     return;
1345   }
1346 
1347   activeres = buddy_getactiveresource(BUDDATA(current_buddy));
1348   if (activeres)
1349     jid = g_strdup_printf("%s/%s", bjid, activeres);
1350   else
1351     jid = g_strdup(bjid);
1352 
1353   send_message_to(jid, msg, subj, type_overwrite, FALSE);
1354   g_free(jid);
1355 }
1356 
scan_mtype(char ** arg)1357 static LmMessageSubType scan_mtype(char **arg)
1358 {
1359   // Try splitting it
1360   char **parlist = split_arg(*arg, 2, 1);
1361   LmMessageSubType result = LM_MESSAGE_SUB_TYPE_NOT_SET;
1362   // Is it a good parameter?
1363   if (parlist && *parlist) {
1364     if (!strcmp("-n", *parlist)) {
1365       result = LM_MESSAGE_SUB_TYPE_NORMAL;
1366     } else if (!strcmp("-h", *parlist)) {
1367       result = LM_MESSAGE_SUB_TYPE_HEADLINE;
1368     }
1369     if (result != LM_MESSAGE_SUB_TYPE_NOT_SET || (!strcmp("--", *parlist)))
1370       *arg += strlen(*arg) - (parlist[1] ? strlen(parlist[1]) : 0);
1371   }
1372   // Anything found? -> skip it
1373   free_arg_lst(parlist);
1374   return result;
1375 }
1376 
say_cmd(char * arg,int parse_flags)1377 void say_cmd(char *arg, int parse_flags)
1378 {
1379   gpointer bud;
1380   LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
1381 
1382   scr_set_chatmode(TRUE);
1383   scr_show_buddy_window();
1384 
1385   if (!current_buddy) {
1386     scr_LogPrint(LPRINT_NORMAL,
1387                  "Whom are you talking to?  Please select a buddy.");
1388     return;
1389   }
1390 
1391   bud = BUDDATA(current_buddy);
1392   if (!(buddy_gettype(bud) &
1393         (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
1394     scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
1395     return;
1396   }
1397 
1398   buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
1399   if (parse_flags)
1400     msgtype = scan_mtype(&arg);
1401   arg = to_utf8(arg);
1402   send_message(arg, NULL, msgtype);
1403   g_free(arg);
1404 }
1405 
do_say(char * arg)1406 static void do_say(char *arg) {
1407   say_cmd(arg, 1);
1408 }
1409 
do_msay(char * arg)1410 static void do_msay(char *arg)
1411 {
1412   /* Parameters: begin verbatim abort send send_to */
1413   char **paramlst;
1414   char *subcmd;
1415 
1416   paramlst = split_arg(arg, 2, 1); // subcmd, arg
1417   subcmd = *paramlst;
1418   arg = *(paramlst+1);
1419 
1420   if (!subcmd || !*subcmd) {
1421     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1422     scr_LogPrint(LPRINT_NORMAL, "Please read the manual before using "
1423                  "the /msay command.");
1424     scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
1425                  "multi-line mode...)", mkcmdstr("msay"));
1426     goto do_msay_return;
1427   }
1428 
1429   if (!strcasecmp(subcmd, "toggle")) {
1430     if (scr_get_multimode())
1431       subcmd = "send";
1432     else
1433       subcmd = "begin";
1434   } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
1435     if (scr_get_multimode())
1436       subcmd = "send";
1437     else
1438       subcmd = "verbatim";
1439   }
1440 
1441   if (!strcasecmp(subcmd, "abort")) {
1442     if (scr_get_multimode())
1443       scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
1444     scr_set_multimode(FALSE, NULL);
1445     goto do_msay_return;
1446   } else if ((!strcasecmp(subcmd, "begin")) ||
1447              (!strcasecmp(subcmd, "verbatim"))) {
1448     bool verbat;
1449     gchar *subj_utf8 = to_utf8(arg);
1450     if (!strcasecmp(subcmd, "verbatim")) {
1451       scr_set_multimode(2, subj_utf8);
1452       verbat = TRUE;
1453     } else {
1454       scr_set_multimode(1, subj_utf8);
1455       verbat = FALSE;
1456     }
1457 
1458     scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.",
1459                  verbat ? "VERBATIM " : "");
1460     scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" "
1461                  "when your message is ready.", mkcmdstr("msay"));
1462     if (verbat)
1463       scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.",
1464                    mkcmdstr("msay"));
1465     g_free(subj_utf8);
1466     goto do_msay_return;
1467   } else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) {
1468     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
1469     goto do_msay_return;
1470   }
1471 
1472   /* send/send_to command */
1473 
1474   if (!scr_get_multimode()) {
1475     scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
1476                  "Use \"%s begin\" first.", mkcmdstr("msay"));
1477     goto do_msay_return;
1478   }
1479 
1480   scr_set_chatmode(TRUE);
1481   scr_show_buddy_window();
1482 
1483   if (!strcasecmp(subcmd, "send_to")) {
1484     int err = FALSE;
1485     gchar *msg_utf8;
1486     LmMessageSubType msg_type = scan_mtype(&arg);
1487     // Let's send to the specified JID.  We leave now if there
1488     // has been an error (so we don't leave multi-line mode).
1489     arg = to_utf8(arg);
1490     msg_utf8 = to_utf8(scr_get_multiline());
1491     if (msg_utf8) {
1492       err = send_message_to(arg, msg_utf8, scr_get_multimode_subj(), msg_type,
1493                             FALSE);
1494       g_free(msg_utf8);
1495     }
1496     g_free(arg);
1497     if (err)
1498       goto do_msay_return;
1499   } else { // Send to currently selected buddy
1500     gpointer bud;
1501     gchar *msg_utf8;
1502 
1503     if (!current_buddy) {
1504       scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
1505       goto do_msay_return;
1506     }
1507 
1508     bud = BUDDATA(current_buddy);
1509     if (!(buddy_gettype(bud) &
1510           (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
1511       scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
1512       goto do_msay_return;
1513     }
1514 
1515     buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
1516     msg_utf8 = to_utf8(scr_get_multiline());
1517     if (msg_utf8) {
1518       send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
1519       g_free(msg_utf8);
1520     }
1521   }
1522   scr_set_multimode(FALSE, NULL);
1523   scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode.");
1524 do_msay_return:
1525   free_arg_lst(paramlst);
1526 }
1527 
1528 //  load_message_from_file(filename)
1529 // Read the whole content of a file.
1530 // The data are converted to UTF8, they should be freed by the caller after
1531 // use.
load_message_from_file(const char * filename)1532 char *load_message_from_file(const char *filename)
1533 {
1534   FILE *fd;
1535   struct stat buf;
1536   char *msgbuf, *msgbuf_utf8;
1537   char *p;
1538   gboolean valid;
1539   size_t len;
1540 
1541   fd = fopen(filename, "r");
1542 
1543   if (!fd || fstat(fileno(fd), &buf)) {
1544     scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
1545     return NULL;
1546   }
1547   if (!buf.st_size || buf.st_size >= HBB_BLOCKSIZE) {
1548     if (!buf.st_size)
1549       scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
1550     else
1551       scr_LogPrint(LPRINT_LOGNORM, "Message file is too big (%s)", filename);
1552     fclose(fd);
1553     return NULL;
1554   }
1555 
1556   msgbuf = g_new0(char, HBB_BLOCKSIZE);
1557   len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd);
1558   fclose(fd);
1559 
1560   // Check there is no binary data.  It must be a *message* file!
1561   valid = TRUE;
1562   if (utf8_mode) {
1563     valid = g_utf8_validate(msgbuf, len, (const gchar **)&p);
1564   } else { // Non-UTF8
1565     for (p = msgbuf ; *p; p++) {
1566       if (!utf8_mode) {
1567         unsigned char sc = *p;
1568         if (!iswprint(sc) && sc != '\n' && sc != '\t') {
1569           valid = FALSE;
1570           break;
1571         }
1572       }
1573     }
1574   }
1575 
1576   if (valid && (*p || p != len+msgbuf)) {
1577     valid = FALSE; // We're not at the End Of Line...
1578   }
1579   if (!valid) {
1580       scr_LogPrint(LPRINT_LOGNORM, "Message file contains "
1581                    "invalid characters (%s)", filename);
1582       g_free(msgbuf);
1583       return NULL;
1584   }
1585 
1586   // p is now at the EOL
1587   // Let's strip trailing newlines
1588   if (p > msgbuf)
1589     p--;
1590   while (p > msgbuf && *p == '\n')
1591     *p-- = 0;
1592 
1593   // It could be empty, once the trailing newlines are gone
1594   if (p == msgbuf && *p == '\n') {
1595     scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
1596     g_free(msgbuf);
1597     return NULL;
1598   }
1599 
1600   msgbuf_utf8 = to_utf8(msgbuf);
1601 
1602   if (!msgbuf_utf8 && msgbuf)
1603     scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)",
1604                  filename);
1605   g_free(msgbuf);
1606   return msgbuf_utf8;
1607 }
1608 
do_say_to(char * arg)1609 static void do_say_to(char *arg)
1610 {
1611   char **paramlst;
1612   char *fjid, *msg_utf8;
1613   char *msg;
1614   char *unescaped_msg = NULL;
1615   char *uncompletedfjid = NULL;
1616   char *file = NULL;
1617   LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
1618   bool quiet = FALSE;
1619   bool eval = FALSE;
1620 
1621   if (!xmpp_is_online()) {
1622     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
1623     return;
1624   }
1625 
1626   msg_type = scan_mtype(&arg);
1627   paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
1628 
1629   if (!*paramlst) {  // No parameter?
1630     scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
1631     free_arg_lst(paramlst);
1632     return;
1633   }
1634 
1635   // Check for an option parameter
1636   while (*paramlst) {
1637     if (!strcmp(*paramlst, "-q")) {
1638       char **oldparamlst = paramlst;
1639       paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
1640       free_arg_lst(oldparamlst);
1641       quiet = TRUE;
1642     } else if (!strcmp(*paramlst, "-e")) {
1643       char **oldparamlst = paramlst;
1644       paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
1645       free_arg_lst(oldparamlst);
1646       eval = TRUE;
1647     } else if (!strcmp(*paramlst, "-f")) {
1648       char **oldparamlst = paramlst;
1649       paramlst = split_arg(*(oldparamlst+1), 2, 1); // filename, jid
1650       free_arg_lst(oldparamlst);
1651       if (!*paramlst) {
1652         scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
1653         free_arg_lst(paramlst);
1654         return;
1655       }
1656       file = g_strdup(*paramlst);
1657       // One more parameter shift...
1658       oldparamlst = paramlst;
1659       paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, nothing
1660       free_arg_lst(oldparamlst);
1661     } else {
1662       break;
1663     }
1664   }
1665 
1666   if (!*paramlst) {
1667     scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
1668     free_arg_lst(paramlst);
1669     return;
1670   }
1671 
1672   fjid = *paramlst;
1673   msg = *(paramlst+1);
1674 
1675   if (fjid[0] == '.') {
1676     const gchar *cjid = (current_buddy ? CURRENT_JID : NULL);
1677     if (fjid[1] == '\0') {
1678       fjid = g_strdup(cjid);
1679     } else if (fjid[1] == JID_RESOURCE_SEPARATOR) {
1680       if (!cjid) {
1681         fjid = NULL;
1682       } else {
1683         char *res_utf8 = to_utf8(fjid+2);
1684         fjid = g_strdup_printf("%s%c%s", cjid, JID_RESOURCE_SEPARATOR, res_utf8);
1685         g_free(res_utf8);
1686       }
1687     } else {
1688       fjid = to_utf8(fjid);
1689     }
1690   } else {
1691     fjid = to_utf8(fjid);
1692   }
1693 
1694   if (!fjid) {
1695     scr_LogPrint(LPRINT_NORMAL, "The Jabber ID is invalid.");
1696     free_arg_lst(paramlst);
1697     return;
1698   }
1699 
1700   if (!strchr(fjid, JID_DOMAIN_SEPARATOR)) {
1701     const gchar *append_server = settings_opt_get("default_server");
1702     if (append_server) {
1703       const char *res = jid_get_resource_name(fjid);
1704       uncompletedfjid = jidtodisp(fjid);
1705       g_free(fjid);
1706       if (res) {
1707         fjid = g_strdup_printf("%s%c%s%c%s", uncompletedfjid, JID_DOMAIN_SEPARATOR,
1708                                append_server, JID_RESOURCE_SEPARATOR, res);
1709       } else {
1710         fjid = g_strdup_printf("%s%c%s", uncompletedfjid, JID_DOMAIN_SEPARATOR,
1711                                append_server);
1712       }
1713     }
1714   }
1715 
1716   if (check_jid_syntax(fjid)) {
1717     scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
1718     free_arg_lst(paramlst);
1719     g_free(uncompletedfjid);
1720     g_free(fjid);
1721     return;
1722   }
1723 
1724   if (!file) {
1725     msg_utf8 = to_utf8(msg);
1726     if (eval) {
1727       unescaped_msg = ut_unescape_tabs_cr(msg_utf8);
1728       // We must not free() if the original string was returned
1729       if (unescaped_msg == msg_utf8)
1730         unescaped_msg = NULL;
1731     }
1732     msg = (unescaped_msg ? unescaped_msg : msg_utf8);
1733   } else {
1734     char *filename_xp;
1735     if (msg)
1736       scr_LogPrint(LPRINT_NORMAL, "say_to: extra parameter ignored.");
1737     filename_xp = expand_filename(file);
1738     msg = msg_utf8 = load_message_from_file(filename_xp);
1739     g_free(filename_xp);
1740     g_free(file);
1741   }
1742 
1743   send_message_to(fjid, msg, NULL, msg_type, quiet);
1744 
1745   g_free(uncompletedfjid);
1746   g_free(fjid);
1747   g_free(msg_utf8);
1748   g_free(unescaped_msg);
1749   free_arg_lst(paramlst);
1750 }
1751 
1752 //  buffer_updown(updown, nblines)
1753 // updown: -1=up, +1=down
buffer_updown(int updown,char * nlines)1754 inline static void buffer_updown(int updown, char *nlines)
1755 {
1756   int nblines;
1757 
1758   if (!nlines || !*nlines)
1759     nblines = 0;
1760   else
1761     nblines = strtol(nlines, NULL, 10);
1762 
1763   if (nblines >= 0)
1764     scr_buffer_scroll_up_down(updown, nblines);
1765 }
1766 
buffer_search(int direction,char * arg)1767 static void buffer_search(int direction, char *arg)
1768 {
1769   if (!arg || !*arg) {
1770     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1771     return;
1772   }
1773 
1774   scr_buffer_search(direction, arg);
1775 }
1776 
buffer_date(char * date)1777 static void buffer_date(char *date)
1778 {
1779   time_t t;
1780 
1781   if (!date || !*date) {
1782     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1783     return;
1784   }
1785 
1786   strip_arg_special_chars(date);
1787 
1788   t = from_iso8601(date, 0);
1789   if (t)
1790     scr_buffer_date(t);
1791   else
1792     scr_LogPrint(LPRINT_NORMAL, "The date you specified is "
1793                  "not correctly formatted or invalid.");
1794 }
1795 
buffer_percent(char * arg1,char * arg2)1796 static void buffer_percent(char *arg1, char *arg2)
1797 {
1798   // Basically, user has typed "%arg1 arg2"
1799   // "%50"  -> arg1 = 50, arg2 null pointer
1800   // "% 50" -> arg1 = \0, arg2 = 50
1801 
1802   if (!*arg1 && (!arg2 || !*arg2)) { // No value
1803     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1804     return;
1805   }
1806 
1807   if (*arg1 && arg2 && *arg2) {     // Two values
1808     scr_LogPrint(LPRINT_NORMAL, "Wrong parameters.");
1809     return;
1810   }
1811 
1812   scr_buffer_percent(atoi((*arg1 ? arg1 : arg2)));
1813 }
1814 
do_buffer(char * arg)1815 static void do_buffer(char *arg)
1816 {
1817   char **paramlst;
1818   char *subcmd;
1819 
1820   if (!current_buddy)
1821     return;
1822 
1823   paramlst = split_arg(arg, 2, 1); // subcmd, arg
1824   subcmd = *paramlst;
1825   arg = *(paramlst+1);
1826 
1827   if (!subcmd || !*subcmd) {
1828     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
1829     free_arg_lst(paramlst);
1830     return;
1831   }
1832 
1833   if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
1834       strcasecmp(subcmd, "close_all")) {
1835     scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer.");
1836     free_arg_lst(paramlst);
1837     return;
1838   }
1839 
1840   if (!strcasecmp(subcmd, "top")) {
1841     scr_buffer_top_bottom(-1);
1842   } else if (!strcasecmp(subcmd, "bottom")) {
1843     scr_buffer_top_bottom(1);
1844   } else if (!strcasecmp(subcmd, "clear")) {
1845     scr_buffer_clear();
1846   } else if (!strcasecmp(subcmd, "close")) {
1847     scr_buffer_purge(1, arg);
1848   } else if (!strcasecmp(subcmd, "close_all")) {
1849     scr_buffer_purge_all(1);
1850   } else if (!strcasecmp(subcmd, "purge")) {
1851     scr_buffer_purge(0, arg);
1852   } else if (!strcasecmp(subcmd, "scroll_lock")) {
1853     scr_buffer_scroll_lock(1);
1854   } else if (!strcasecmp(subcmd, "scroll_unlock")) {
1855     scr_buffer_scroll_lock(0);
1856   } else if (!strcasecmp(subcmd, "scroll_toggle")) {
1857     scr_buffer_scroll_lock(-1);
1858   } else if (!strcasecmp(subcmd, "up")) {
1859     buffer_updown(-1, arg);
1860   } else if (!strcasecmp(subcmd, "down")) {
1861     buffer_updown(1, arg);
1862   } else if (!strcasecmp(subcmd, "search_backward")) {
1863     strip_arg_special_chars(arg);
1864     buffer_search(-1, arg);
1865   } else if (!strcasecmp(subcmd, "search_forward")) {
1866     strip_arg_special_chars(arg);
1867     buffer_search(1, arg);
1868   } else if (!strcasecmp(subcmd, "date")) {
1869     buffer_date(arg);
1870   } else if (*subcmd == '%') {
1871     buffer_percent(subcmd+1, arg);
1872   } else if (!strcasecmp(subcmd, "save")) {
1873     scr_buffer_dump(arg);
1874   } else if (!strcasecmp(subcmd, "list")) {
1875     scr_buffer_list();
1876   } else if (!strcasecmp(subcmd, "readmark")) {
1877     scr_buffer_jump_readmark();
1878   } else {
1879     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
1880   }
1881 
1882   free_arg_lst(paramlst);
1883 }
1884 
do_clear(char * arg)1885 static void do_clear(char *arg)    // Alias for "buffer clear"
1886 {
1887   do_buffer("clear");
1888 }
1889 
do_info(char * arg)1890 static void do_info(char *arg)
1891 {
1892   gpointer bud;
1893   const char *bjid, *name;
1894   guint type, on_srv;
1895   char *buffer;
1896   enum subscr esub;
1897 
1898   if (!current_buddy)
1899     return;
1900   bud = BUDDATA(current_buddy);
1901 
1902   bjid   = buddy_getjid(bud);
1903   name   = buddy_getname(bud);
1904   type   = buddy_gettype(bud);
1905   esub   = buddy_getsubscription(bud);
1906   on_srv = buddy_getonserverflag(bud);
1907 
1908   buffer = g_new(char, 4096);
1909 
1910   if (bjid) {
1911     GSList *resources, *p_res;
1912     char *bstr = "unknown";
1913 
1914     // Enter chat mode
1915     scr_set_chatmode(TRUE);
1916     scr_show_buddy_window();
1917 
1918     snprintf(buffer, 4095, "jid:  <%s>", bjid);
1919     scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1920     if (name) {
1921       snprintf(buffer, 4095, "Name: %s", name);
1922       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1923     }
1924 
1925     if (type == ROSTER_TYPE_USER)       bstr = "user";
1926     else if (type == ROSTER_TYPE_ROOM)  bstr = "chatroom";
1927     else if (type == ROSTER_TYPE_AGENT) bstr = "agent";
1928     snprintf(buffer, 127, "Type: %s", bstr);
1929     scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1930 
1931     if (!on_srv) {
1932       scr_WriteIncomingMessage(bjid, "(Local item, not on the server)",
1933                                0, HBB_PREFIX_INFO, 0);
1934     }
1935 
1936     if (esub == sub_both)     bstr = "both";
1937     else if (esub & sub_from) bstr = "from";
1938     else if (esub & sub_to)   bstr = "to";
1939     else bstr = "none";
1940     snprintf(buffer, 64, "Subscription: %s", bstr);
1941     if (esub & sub_pending)
1942       strcat(buffer, " (pending)");
1943     scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1944 
1945     resources = buddy_getresources(bud);
1946     if (!resources && type == ROSTER_TYPE_USER) {
1947       // No resource; display last status message, if any.
1948       const char *rst_msg = buddy_getstatusmsg(bud, "");
1949       if (rst_msg) {
1950         snprintf(buffer, 4095, "Last status message: %s", rst_msg);
1951         scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1952       }
1953     }
1954     for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
1955       gchar rprio;
1956       enum imstatus rstatus;
1957       const char *rst_msg;
1958       time_t rst_time;
1959 
1960       rprio   = buddy_getresourceprio(bud, p_res->data);
1961       rstatus = buddy_getstatus(bud, p_res->data);
1962       rst_msg = buddy_getstatusmsg(bud, p_res->data);
1963       rst_time = buddy_getstatustime(bud, p_res->data);
1964 
1965       snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus],
1966                rprio, (char*)p_res->data);
1967       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
1968       if (rst_msg) {
1969         snprintf(buffer, 4095, "Status message: %s", rst_msg);
1970         scr_WriteIncomingMessage(bjid, buffer,
1971                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
1972       }
1973       if (rst_time) {
1974         char tbuf[128];
1975 
1976         strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
1977         snprintf(buffer, 4095, "Status timestamp: %s", tbuf);
1978         scr_WriteIncomingMessage(bjid, buffer,
1979                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
1980       }
1981 #ifdef HAVE_GPGME
1982       struct pgp_data *rpgp = buddy_resource_pgp(bud, p_res->data);
1983 
1984       if (rpgp && rpgp->sign_keyid) {
1985         snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid);
1986         scr_WriteIncomingMessage(bjid, buffer,
1987                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
1988         if (rpgp->last_sigsum) {
1989           gpgme_sigsum_t ss = rpgp->last_sigsum;
1990           snprintf(buffer, 4095, "Last PGP signature: %s",
1991                   (ss & GPGME_SIGSUM_GREEN ? "good":
1992                    (ss & GPGME_SIGSUM_RED ? "bad" : "unknown")));
1993           scr_WriteIncomingMessage(bjid, buffer,
1994                                    0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
1995         }
1996       }
1997 #endif
1998       g_free(p_res->data);
1999     }
2000     g_slist_free(resources);
2001   } else {  /* Item has no jid */
2002     if (name) scr_LogPrint(LPRINT_NORMAL, "Name: %s", name);
2003     scr_LogPrint(LPRINT_NORMAL, "Type: %s",
2004                  type == ROSTER_TYPE_GROUP ? "group" :
2005                  (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown"));
2006   }
2007   g_free(buffer);
2008 
2009   // Tell the user if this item has an annotation.
2010   if (type == ROSTER_TYPE_USER ||
2011       type == ROSTER_TYPE_ROOM ||
2012       type == ROSTER_TYPE_AGENT) {
2013     struct annotation *note = xmpp_get_storage_rosternotes(bjid, TRUE);
2014     if (note) {
2015       // We do not display the note, we just tell the user.
2016       g_free(note->text);
2017       g_free(note->jid);
2018       g_free(note);
2019       scr_WriteIncomingMessage(bjid, "(This item has an annotation)", 0,
2020                                HBB_PREFIX_INFO, 0);
2021     }
2022   }
2023 }
2024 
2025 // room_names() is a variation of do_info(), for chatrooms only
room_names(gpointer bud,char * arg)2026 static void room_names(gpointer bud, char *arg)
2027 {
2028   const char *bjid;
2029   char *buffer;
2030   GSList *resources, *p_res;
2031   enum { style_normal = 0, style_detail, style_short,
2032          style_quiet, style_compact } style = 0;
2033   int cnt = 0;
2034 
2035   if (*arg) {
2036     if (!strcasecmp(arg, "--short")) {
2037       style = style_short;
2038     } else if (!strcasecmp(arg, "--quiet")) {
2039       style = style_quiet;
2040     } else if (!strcasecmp(arg, "--detail")) {
2041       style = style_detail;
2042     } else if (!strcasecmp(arg, "--compact")) {
2043       style = style_compact;
2044     } else {
2045       scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
2046       return;
2047     }
2048   }
2049 
2050   // Enter chat mode
2051   scr_set_chatmode(TRUE);
2052   scr_show_buddy_window();
2053 
2054   bjid = buddy_getjid(bud);
2055 
2056   buffer = g_new(char, 4096);
2057   strncpy(buffer, "Room members:", 127);
2058   scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
2059 
2060   resources = buddy_getresources(bud);
2061   for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
2062     enum imstatus rstatus;
2063     const char *rst_msg;
2064     cnt++;
2065 
2066     rstatus = buddy_getstatus(bud, p_res->data);
2067     rst_msg = buddy_getstatusmsg(bud, p_res->data);
2068 
2069     if (style == style_short) {
2070       snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
2071                (char*)p_res->data,
2072                rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
2073       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
2074     } else if (style == style_compact) {
2075         enum imrole role = buddy_getrole(bud, p_res->data);
2076         enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
2077         bool showaffil = (affil != affil_none);
2078 
2079         snprintf(buffer, 4095, "[%c] %s (%s%s%s)",
2080                  imstatus2char[rstatus], (char*)p_res->data,
2081                  showaffil ? straffil[affil] : "\0",
2082                  showaffil ? "/" : "\0",
2083                  strrole[role]);
2084         scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
2085       } else {
2086       // (Style "normal", "detail" or "quiet")
2087       snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
2088                (char*)p_res->data);
2089       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
2090       if (rst_msg && style != style_quiet) {
2091         snprintf(buffer, 4095, "Status message: %s", rst_msg);
2092         scr_WriteIncomingMessage(bjid, buffer,
2093                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
2094       }
2095       if (style == style_detail) {
2096         enum imrole role = buddy_getrole(bud, p_res->data);
2097         enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
2098 
2099         snprintf(buffer, 4095, "Role: %s", strrole[role]);
2100         scr_WriteIncomingMessage(bjid, buffer,
2101                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
2102         if (affil != affil_none) {
2103           snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
2104           scr_WriteIncomingMessage(bjid, buffer,
2105                                    0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
2106         }
2107       }
2108     }
2109     g_free(p_res->data);
2110   }
2111 
2112   snprintf(buffer, 4095, "Total: %d member%c", cnt, cnt > 1 ? 's' : '\0');
2113   scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
2114 
2115   g_slist_free(resources);
2116   g_free(buffer);
2117 }
2118 
move_group_member(gpointer bud,void * groupnamedata)2119 static void move_group_member(gpointer bud, void *groupnamedata)
2120 {
2121   const char *bjid, *name, *groupname;
2122   guint type, on_srv;
2123 
2124   groupname = (char *)groupnamedata;
2125 
2126   bjid = buddy_getjid(bud);
2127   name = buddy_getname(bud);
2128   type = buddy_gettype(bud);
2129   on_srv = buddy_getonserverflag(bud);
2130 
2131   if (on_srv) {
2132     xmpp_updatebuddy(bjid, name, *groupname ? groupname : NULL);
2133   } else {
2134     buddy_setgroup(bud, (char *)groupname);
2135     if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
2136         settings_opt_get_int("muc_bookmark_autoupdate"))
2137       room_bookmark(bud, NULL);
2138   }
2139 }
2140 
do_rename(char * arg)2141 static void do_rename(char *arg)
2142 {
2143   gpointer bud;
2144   const char *bjid, *group;
2145   guint type, on_srv;
2146   char *newname, *p;
2147   char *name_utf8;
2148 
2149   if (!current_buddy)
2150     return;
2151   bud = BUDDATA(current_buddy);
2152 
2153   bjid   = buddy_getjid(bud);
2154   group  = buddy_getgroupname(bud);
2155   type   = buddy_gettype(bud);
2156   on_srv = buddy_getonserverflag(bud);
2157 
2158   if (type & ROSTER_TYPE_SPECIAL) {
2159     scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
2160     return;
2161   }
2162 
2163   if (!*arg && !(type & ROSTER_TYPE_GROUP)) {
2164     scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
2165     return;
2166   }
2167 
2168   //if (!(type & ROSTER_TYPE_GROUP) && !on_srv) {
2169   //  scr_LogPrint(LPRINT_NORMAL,
2170   //               "Note: this item will be added to your server roster.");
2171   //  // If this is a MUC room w/o bookmark, let's give a small hint...
2172   //  if ((type & ROSTER_TYPE_ROOM) && !xmpp_is_bookmarked(bjid)) {
2173   //    scr_LogPrint(LPRINT_NORMAL,
2174   //                 "You should add a room bookmark or it will not be "
2175   //                 "recognized as a MUC room next time you run mcabber.");
2176   //  }
2177   //}
2178 
2179   newname = g_strdup(arg);
2180   // Remove trailing space
2181   for (p = newname; *p; p++) ;
2182   while (p > newname && *p == ' ') *p = 0;
2183 
2184   strip_arg_special_chars(newname);
2185 
2186   name_utf8 = to_utf8(newname);
2187 
2188   if (type & ROSTER_TYPE_GROUP) {
2189     // Rename a whole group
2190     foreach_group_member(bud, &move_group_member, name_utf8);
2191     // Let's jump to the previous buddy, because this group name should
2192     // disappear when we receive the server answer.
2193     scr_roster_up_down(-1, 1);
2194   } else {
2195     // Rename a single buddy
2196     guint del_name = 0;
2197     if (!*newname || !strcmp(arg, "-"))
2198       del_name = TRUE;
2199     if (on_srv) {
2200       /* We do not rename the buddy right now because the server could reject
2201        * the request.  Let's wait for the server answer.
2202        */
2203       xmpp_updatebuddy(bjid, (del_name ? NULL : name_utf8),
2204                        group && *group ? group : NULL);
2205     } else {
2206       // This is a local item, we rename it without adding to roster.
2207       buddy_setname(bud, (del_name ? (char*)bjid : name_utf8));
2208       if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
2209           settings_opt_get_int("muc_bookmark_autoupdate"))
2210         room_bookmark(bud, NULL);
2211     }
2212   }
2213 
2214   g_free(name_utf8);
2215   g_free(newname);
2216   scr_update_roster();
2217 }
2218 
do_move(char * arg)2219 static void do_move(char *arg)
2220 {
2221   gpointer bud;
2222   const char *bjid, *name, *oldgroupname;
2223   guint type, on_srv;
2224   char *newgroupname, *p;
2225   char *group_utf8;
2226 
2227   if (!current_buddy)
2228     return;
2229   bud = BUDDATA(current_buddy);
2230 
2231   bjid = buddy_getjid(bud);
2232   name = buddy_getname(bud);
2233   type = buddy_gettype(bud);
2234   on_srv = buddy_getonserverflag(bud);
2235 
2236   oldgroupname = buddy_getgroupname(bud);
2237 
2238   if (type & ROSTER_TYPE_GROUP) {
2239     scr_LogPrint(LPRINT_NORMAL, "You can't move groups!");
2240     return;
2241   }
2242   if (type & ROSTER_TYPE_SPECIAL) {
2243     scr_LogPrint(LPRINT_NORMAL, "You can't move this item.");
2244     return;
2245   }
2246 
2247   newgroupname = g_strdup(arg);
2248   // Remove trailing space
2249   for (p = newgroupname; *p; p++) ;
2250   while (p > newgroupname && *p == ' ') *p-- = 0;
2251 
2252   strip_arg_special_chars(newgroupname);
2253 
2254   group_utf8 = to_utf8(newgroupname);
2255   if (strcmp(oldgroupname, group_utf8)) {
2256     if (on_srv) {
2257       xmpp_updatebuddy(bjid, name, *group_utf8 ? group_utf8 : NULL);
2258       scr_roster_up_down(-1, 1);
2259 
2260       /* We do not move the buddy right now because the server could reject
2261        * the request.  Let's wait for the server answer.
2262        */
2263     } else {
2264       // This is a local item, we move it without adding to roster.
2265       guint msgflag;
2266 
2267       // If the buddy has a pending message flag,
2268       // we remove it temporarily in order to reset the global group
2269       // flag.  We set it back once the room is in the new group,
2270       // which will update the new group's flag.
2271       msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
2272       if (msgflag)
2273         roster_msg_setflag(bjid, FALSE, FALSE);
2274       buddy_setgroup(bud, group_utf8);
2275       if (msgflag)
2276         roster_msg_setflag(bjid, FALSE, TRUE);
2277       if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
2278           settings_opt_get_int("muc_bookmark_autoupdate"))
2279         room_bookmark(bud, NULL);
2280     }
2281   }
2282 
2283   g_free(group_utf8);
2284   g_free(newgroupname);
2285   scr_update_roster();
2286 }
2287 
list_option_cb(char * k,char * v,void * f)2288 static void list_option_cb(char *k, char *v, void *f)
2289 {
2290   if (strncmp(k, "password", 8) && strcmp(k, "pgp_passphrase")) {
2291     GSList **list = f;
2292     *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
2293   }
2294 }
2295 
do_set(char * arg)2296 static void do_set(char *arg)
2297 {
2298   guint assign;
2299   gchar *option, *value;
2300   gchar *option_utf8;
2301 
2302   if (!*arg) {
2303     // List all set options
2304     gsize max = 0;
2305     gsize maxmax = scr_gettextwidth() / 3;
2306     GSList *lel;
2307     gchar *format;
2308     GSList *list = NULL;
2309     // Get sorted list of keys
2310     settings_foreach(SETTINGS_TYPE_OPTION, list_option_cb, &list);
2311     if (!list) {
2312       scr_LogPrint(LPRINT_NORMAL, "No options found.");
2313       return;
2314     }
2315     // Find out maximum key length
2316     for (lel = list; lel; lel = lel->next) {
2317       const gchar *key = lel->data;
2318       gsize len = strlen(key);
2319       if (len > max) {
2320         max = len;
2321         if (max > maxmax) {
2322           max = maxmax;
2323           break;
2324         }
2325       }
2326     }
2327     // Print out list of options
2328     format = g_strdup_printf("%%-%us = [%%s]", (unsigned)max);
2329     for (lel = list; lel; lel = lel->next) {
2330       const gchar *key = lel->data;
2331       scr_LogPrint(LPRINT_NORMAL, format, key, settings_opt_get(key));
2332     }
2333     g_free(format);
2334     scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
2335     scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
2336                                ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
2337     return;
2338   }
2339 
2340   assign = parse_assigment(arg, &option, &value);
2341   if (!option) {
2342     scr_LogPrint(LPRINT_NORMAL, "Set what option?");
2343     return;
2344   }
2345   option_utf8 = to_utf8(option);
2346   g_free(option);
2347   if (!assign) {  // This is a query
2348     const gchar *val = settings_opt_get(option_utf8);
2349     if (val) {
2350       if (g_ascii_strncasecmp(option_utf8, "password", 8) == 0 ||
2351           g_ascii_strcasecmp(option_utf8, "pgp_passphrase") == 0)
2352         val = "***";
2353       scr_LogPrint(LPRINT_NORMAL, "%s = [%s]", option_utf8, val);
2354     } else {
2355       scr_LogPrint(LPRINT_NORMAL, "Option %s is not set", option_utf8);
2356     }
2357     g_free(option_utf8);
2358     return;
2359   }
2360   // Update the option
2361   // Maybe some options should be protected when user is connected (server,
2362   // username, etc.).  And we should catch some options here, too
2363   // (hide_offline_buddies for ex.)
2364   if (!value) {
2365     settings_del(SETTINGS_TYPE_OPTION, option_utf8);
2366   } else {
2367     gchar *value_utf8 = to_utf8(value);
2368     settings_set(SETTINGS_TYPE_OPTION, option_utf8, value_utf8);
2369     g_free(value_utf8);
2370     g_free(value);
2371   }
2372   g_free(option_utf8);
2373 }
2374 
dump_alias(char * k,char * v,void * param)2375 static void dump_alias(char *k, char *v, void *param)
2376 {
2377   scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "Alias %s = %s", k, v);
2378   scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
2379   scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
2380                                  ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
2381 }
2382 
do_alias(char * arg)2383 static void do_alias(char *arg)
2384 {
2385   guint assign;
2386   gchar *alias, *value;
2387 
2388   assign = parse_assigment(arg, &alias, &value);
2389   if (!alias) {
2390     settings_foreach(SETTINGS_TYPE_ALIAS, &dump_alias, NULL);
2391     scr_update_roster();
2392     return;
2393   }
2394   if (!assign) {  // This is a query
2395     const char *val = settings_get(SETTINGS_TYPE_ALIAS, alias);
2396     // NOTE: LPRINT_NOTUTF8 here, see below why it isn't encoded...
2397     if (val)
2398       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "%s = %s", alias, val);
2399     else
2400       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
2401                    "Alias '%s' does not exist", alias);
2402     goto do_alias_return;
2403   }
2404   // Check the alias does not conflict with a registered command
2405   if (cmd_get(alias)) {
2406       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
2407                    "'%s' is a reserved word!", alias);
2408       goto do_alias_return;
2409   }
2410   // Update the alias
2411   if (!value) {
2412     if (settings_get(SETTINGS_TYPE_ALIAS, alias)) {
2413       settings_del(SETTINGS_TYPE_ALIAS, alias);
2414       // Remove alias from the completion list
2415       compl_del_category_word(COMPL_CMD, alias);
2416     }
2417   } else {
2418     /* Add alias to the completion list, if not already in.
2419        NOTE: We're not UTF8-encoding "alias" and "value" here because UTF-8 is
2420        not yet supported in the UI... (and we use the values in the completion
2421        system)
2422     */
2423     if (!settings_get(SETTINGS_TYPE_ALIAS, alias))
2424       compl_add_category_word(COMPL_CMD, alias);
2425     settings_set(SETTINGS_TYPE_ALIAS, alias, value);
2426     g_free(value);
2427   }
2428 do_alias_return:
2429   g_free(alias);
2430 }
2431 
dump_bind(char * k,char * v,void * param)2432 static void dump_bind(char *k, char *v, void *param)
2433 {
2434   scr_LogPrint(LPRINT_NORMAL, "Key %4s is bound to: %s", k, v);
2435 }
2436 
do_bind(char * arg)2437 static void do_bind(char *arg)
2438 {
2439   guint assign;
2440   gchar *k_code, *value;
2441 
2442   assign = parse_assigment(arg, &k_code, &value);
2443   if (!k_code) {
2444     settings_foreach(SETTINGS_TYPE_BINDING, &dump_bind, NULL);
2445     scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
2446     scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
2447                                    ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
2448     return;
2449   }
2450   if (!assign) {  // This is a query
2451     const char *val = settings_get(SETTINGS_TYPE_BINDING, k_code);
2452     if (val)
2453       scr_LogPrint(LPRINT_NORMAL, "Key %s is bound to: %s", k_code, val);
2454     else
2455       scr_LogPrint(LPRINT_NORMAL, "Key %s is not bound.", k_code);
2456     g_free(k_code);
2457     return;
2458   }
2459   // Update the key binding
2460   if (!value) {
2461     settings_del(SETTINGS_TYPE_BINDING, k_code);
2462   } else {
2463     gchar *value_utf8 = to_utf8(value);
2464     settings_set(SETTINGS_TYPE_BINDING, k_code, value_utf8);
2465     g_free(value_utf8);
2466     g_free(value);
2467   }
2468   g_free(k_code);
2469 }
2470 
do_quit(char * arg)2471 static void do_quit(char *arg)
2472 {
2473   mcabber_set_terminate_ui();
2474 }
2475 
do_rawxml(char * arg)2476 static void do_rawxml(char *arg)
2477 {
2478   char **paramlst;
2479   char *subcmd;
2480 
2481   if (!xmpp_is_online()) {
2482     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
2483     return;
2484   }
2485 
2486   paramlst = split_arg(arg, 2, 1); // subcmd, arg
2487   subcmd = *paramlst;
2488   arg = *(paramlst+1);
2489 
2490   if (!subcmd || !*subcmd) {
2491     scr_LogPrint(LPRINT_NORMAL, "Please read the manual page"
2492                  " before using /rawxml :-)");
2493     free_arg_lst(paramlst);
2494     return;
2495   }
2496 
2497   if (!strcasecmp(subcmd, "send"))  {
2498     gchar *buffer;
2499 
2500     if (!subcmd || !*subcmd) {
2501       scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
2502       free_arg_lst(paramlst);
2503       return;
2504     }
2505 
2506     // We don't strip_arg_special_chars() here, because it would be a pain for
2507     // the user to escape quotes in a XML stream...
2508 
2509     buffer = to_utf8(arg);
2510     if (buffer) {
2511       scr_LogPrint(LPRINT_NORMAL, "Sending XML string");
2512       lm_connection_send_raw(lconnection, buffer, NULL);
2513       g_free(buffer);
2514     } else {
2515       scr_LogPrint(LPRINT_NORMAL, "Conversion error in XML string.");
2516     }
2517   } else {
2518     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
2519   }
2520 
2521   free_arg_lst(paramlst);
2522 }
2523 
2524 //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
2525 // - Check if this is a room, if buddy_must_be_a_room is not null
2526 // - Check there is at least 1 parameter, if param_needed is true
2527 // - Return null if one of the checks fails, or a pointer to the first
2528 //   non-space character.
check_room_subcommand(char * arg,bool param_needed,gpointer buddy_must_be_a_room)2529 static char *check_room_subcommand(char *arg, bool param_needed,
2530                                    gpointer buddy_must_be_a_room)
2531 {
2532   if (buddy_must_be_a_room &&
2533       !(buddy_gettype(buddy_must_be_a_room) & ROSTER_TYPE_ROOM)) {
2534     scr_LogPrint(LPRINT_NORMAL, "This isn't a conference room.");
2535     return NULL;
2536   }
2537 
2538   if (param_needed) {
2539     if (!arg) {
2540       scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
2541       return NULL;
2542     }
2543   }
2544 
2545   if (arg)
2546     return arg;
2547   else
2548     return "";
2549 }
2550 
room_join(gpointer bud,char * arg)2551 static void room_join(gpointer bud, char *arg)
2552 {
2553   char **paramlst;
2554   char *roomname, *nick, *pass;
2555   char *roomname_tmp = NULL;
2556   char *pass_utf8;
2557 
2558   paramlst = split_arg(arg, 3, 0); // roomid, nickname, password
2559   roomname = *paramlst;
2560   nick = *(paramlst+1);
2561   pass = *(paramlst+2);
2562 
2563   if (!roomname)
2564     nick = NULL;
2565   if (!nick)
2566     pass = NULL;
2567 
2568   if (!roomname || !strcmp(roomname, ".")) {
2569     // If the current_buddy is recognized as a room, the room name
2570     // can be omitted (or "." can be used).
2571     if (!bud || !(buddy_gettype(bud) & ROSTER_TYPE_ROOM)) {
2572       scr_LogPrint(LPRINT_NORMAL, "Please specify a room name.");
2573       free_arg_lst(paramlst);
2574       return;
2575     }
2576     roomname = (char*)buddy_getjid(bud);
2577   } else if (strchr(roomname, '/')) {
2578     scr_LogPrint(LPRINT_NORMAL, "Invalid room name.");
2579     free_arg_lst(paramlst);
2580     return;
2581   } else {
2582     // The room id has been specified.  Let's convert it and use it.
2583     mc_strtolower(roomname);
2584     roomname = roomname_tmp = to_utf8(roomname);
2585   }
2586 
2587   // If no nickname is provided with the /join command,
2588   // we try to get a default nickname.
2589   if (!nick || !*nick)
2590     nick = default_muc_nickname(roomname);
2591   else
2592     nick = to_utf8(nick);
2593   // If we still have no nickname, give up
2594   if (!nick || !*nick) {
2595     scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
2596     g_free(nick);
2597     free_arg_lst(paramlst);
2598     return;
2599   }
2600 
2601   pass_utf8 = to_utf8(pass);
2602 
2603   if (!pass) {
2604     const char *roompass = xmpp_get_bookmark_password(roomname);
2605     if (roompass)
2606       pass_utf8 = g_strdup(roompass);
2607   }
2608 
2609   xmpp_room_join(roomname, nick, pass_utf8);
2610 
2611   scr_LogPrint(LPRINT_LOGNORM, "Sent a join request to <%s>...", roomname);
2612 
2613   g_free(roomname_tmp);
2614   g_free(nick);
2615   g_free(pass_utf8);
2616   buddylist_defer_build();
2617   scr_update_roster();
2618   free_arg_lst(paramlst);
2619 }
2620 
room_invite(gpointer bud,char * arg)2621 static void room_invite(gpointer bud, char *arg)
2622 {
2623   char **paramlst;
2624   const gchar *roomname;
2625   char* fjid;
2626   gchar *reason_utf8;
2627 
2628   paramlst = split_arg(arg, 2, 1); // jid, [reason]
2629   fjid = *paramlst;
2630   arg = *(paramlst+1);
2631   // An empty reason is no reason...
2632   if (arg && !*arg)
2633     arg = NULL;
2634 
2635   if (!fjid || !*fjid) {
2636     scr_LogPrint(LPRINT_NORMAL, "Missing or incorrect Jabber ID.");
2637     free_arg_lst(paramlst);
2638     return;
2639   }
2640 
2641   roomname = buddy_getjid(bud);
2642   reason_utf8 = to_utf8(arg);
2643   xmpp_room_invite(roomname, fjid, reason_utf8);
2644   scr_LogPrint(LPRINT_LOGNORM, "Invitation sent to <%s>.", fjid);
2645   g_free(reason_utf8);
2646   free_arg_lst(paramlst);
2647 }
2648 
room_affil(gpointer bud,char * arg)2649 static void room_affil(gpointer bud, char *arg)
2650 {
2651   char **paramlst;
2652   gchar *fjid, *rolename;
2653   struct role_affil ra;
2654   const char *roomid = buddy_getjid(bud);
2655 
2656   paramlst = split_arg(arg, 3, 1); // jid, new_affil, [reason]
2657   fjid = *paramlst;
2658   rolename = *(paramlst+1);
2659   arg = *(paramlst+2);
2660 
2661   if (!fjid || !*fjid || !rolename || !*rolename) {
2662     scr_LogPrint(LPRINT_NORMAL, "Please specify both a Jabber ID and a role.");
2663     free_arg_lst(paramlst);
2664     return;
2665   }
2666 
2667   ra.type = type_affil;
2668   ra.val.affil = affil_none;
2669   for (; ra.val.affil < imaffiliation_size; ra.val.affil++)
2670     if (!strcasecmp(rolename, straffil[ra.val.affil]))
2671       break;
2672 
2673   if (ra.val.affil < imaffiliation_size) {
2674     gchar *jid_utf8, *reason_utf8;
2675     jid_utf8 = to_utf8(fjid);
2676     reason_utf8 = to_utf8(arg);
2677     xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, reason_utf8);
2678     g_free(jid_utf8);
2679     g_free(reason_utf8);
2680   } else {
2681     scr_LogPrint(LPRINT_NORMAL, "Wrong affiliation parameter.");
2682   }
2683 
2684   free_arg_lst(paramlst);
2685 }
2686 
room_role(gpointer bud,char * arg)2687 static void room_role(gpointer bud, char *arg)
2688 {
2689   char **paramlst;
2690   gchar *nick, *rolename;
2691   struct role_affil ra;
2692   const char *roomid = buddy_getjid(bud);
2693 
2694   paramlst = split_arg(arg, 3, 1); // nick, new_role, [reason]
2695   nick = *paramlst;
2696   rolename = *(paramlst+1);
2697   arg = *(paramlst+2);
2698 
2699   if (!nick || !*nick || !rolename || !*rolename) {
2700     scr_LogPrint(LPRINT_NORMAL, "Please specify both a nickname and a role.");
2701     free_arg_lst(paramlst);
2702     return;
2703   }
2704 
2705   ra.type = type_role;
2706   ra.val.role = role_none;
2707   for (; ra.val.role < imrole_size; ra.val.role++)
2708     if (!strcasecmp(rolename, strrole[ra.val.role]))
2709       break;
2710 
2711   if (ra.val.role < imrole_size) {
2712     gchar *nick_utf8, *reason_utf8;
2713     nick_utf8 = to_utf8(nick);
2714     reason_utf8 = to_utf8(arg);
2715     xmpp_room_setattrib(roomid, NULL, nick_utf8, ra, reason_utf8);
2716     g_free(nick_utf8);
2717     g_free(reason_utf8);
2718   } else {
2719     scr_LogPrint(LPRINT_NORMAL, "Wrong role parameter.");
2720   }
2721 
2722   free_arg_lst(paramlst);
2723 }
2724 
2725 
2726 // The expected argument is a Jabber id
room_ban(gpointer bud,char * arg)2727 static void room_ban(gpointer bud, char *arg)
2728 {
2729   char **paramlst;
2730   gchar *fjid, *bjid;
2731   const gchar *banjid;
2732   gchar *jid_utf8, *reason_utf8;
2733   struct role_affil ra;
2734   const char *roomid = buddy_getjid(bud);
2735 
2736   paramlst = split_arg(arg, 2, 1); // jid, [reason]
2737   fjid = *paramlst;
2738   arg = *(paramlst+1);
2739 
2740   if (!fjid || !*fjid) {
2741     scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
2742     free_arg_lst(paramlst);
2743     return;
2744   }
2745 
2746   ra.type = type_affil;
2747   ra.val.affil = affil_outcast;
2748 
2749   bjid = jidtodisp(fjid);
2750   jid_utf8 = to_utf8(bjid);
2751 
2752   // If the argument doesn't look like a jid, we'll try to find a matching
2753   // nickname.
2754   if (!strchr(bjid, JID_DOMAIN_SEPARATOR) || check_jid_syntax(bjid)) {
2755     const gchar *tmp;
2756     // We want the initial argument, so the fjid variable, because
2757     // we don't want to strip a resource-like string from the nickname!
2758     g_free(jid_utf8);
2759     jid_utf8 = to_utf8(fjid);
2760     tmp = buddy_getrjid(bud, jid_utf8);
2761     if (!tmp) {
2762       scr_LogPrint(LPRINT_NORMAL, "Wrong JID or nickname");
2763       goto room_ban_return;
2764     }
2765     banjid = jidtodisp(tmp);
2766   } else {
2767     banjid = jid_utf8;
2768   }
2769 
2770   scr_LogPrint(LPRINT_NORMAL, "Requesting a ban for %s", banjid);
2771 
2772   reason_utf8 = to_utf8(arg);
2773   xmpp_room_setattrib(roomid, banjid, NULL, ra, reason_utf8);
2774   g_free(reason_utf8);
2775 
2776 room_ban_return:
2777   g_free(bjid);
2778   g_free(jid_utf8);
2779   free_arg_lst(paramlst);
2780 }
2781 
2782 // The expected argument is a Jabber id
room_unban(gpointer bud,char * arg)2783 static void room_unban(gpointer bud, char *arg)
2784 {
2785   gchar *fjid = arg;
2786   gchar *jid_utf8;
2787   struct role_affil ra;
2788   const char *roomid = buddy_getjid(bud);
2789 
2790   if (!fjid || !*fjid) {
2791     scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
2792     return;
2793   }
2794 
2795   ra.type = type_affil;
2796   ra.val.affil = affil_none;
2797 
2798   jid_utf8 = to_utf8(fjid);
2799   xmpp_room_setattrib(roomid, jid_utf8, NULL, ra, NULL);
2800   g_free(jid_utf8);
2801 }
2802 
2803 // The expected argument is a nickname
room_kick(gpointer bud,char * arg)2804 static void room_kick(gpointer bud, char *arg)
2805 {
2806   char **paramlst;
2807   gchar *nick;
2808   gchar *nick_utf8, *reason_utf8;
2809   struct role_affil ra;
2810   const char *roomid = buddy_getjid(bud);
2811 
2812   paramlst = split_arg(arg, 2, 1); // nickname, [reason]
2813   nick = *paramlst;
2814   arg = *(paramlst+1);
2815 
2816   if (!nick || !*nick) {
2817     scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
2818     free_arg_lst(paramlst);
2819     return;
2820   }
2821 
2822   ra.type = type_role;
2823   ra.val.role = role_none;
2824 
2825   nick_utf8 = to_utf8(nick);
2826   reason_utf8 = to_utf8(arg);
2827   xmpp_room_setattrib(roomid, NULL, nick_utf8, ra, reason_utf8);
2828   g_free(nick_utf8);
2829   g_free(reason_utf8);
2830 
2831   free_arg_lst(paramlst);
2832 }
2833 
cmd_room_leave(gpointer bud,char * arg)2834 void cmd_room_leave(gpointer bud, char *arg)
2835 {
2836   gchar *roomid, *desc;
2837   const char *nickname;
2838 
2839   nickname = buddy_getnickname(bud);
2840   if (!nickname) {
2841     scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
2842     return;
2843   }
2844 
2845   roomid = g_strdup_printf("%s/%s", buddy_getjid(bud), nickname);
2846   desc = to_utf8(arg);
2847   xmpp_setstatus(offline, roomid, desc, TRUE);
2848   g_free(desc);
2849   g_free(roomid);
2850 }
2851 
room_nick(gpointer bud,char * arg)2852 static void room_nick(gpointer bud, char *arg)
2853 {
2854   if (!buddy_getinsideroom(bud)) {
2855     scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
2856     return;
2857   }
2858 
2859   if (!arg || !*arg) {
2860     const char *nick = buddy_getnickname(bud);
2861     if (nick)
2862       scr_LogPrint(LPRINT_NORMAL, "Your nickname is: %s", nick);
2863     else
2864       scr_LogPrint(LPRINT_NORMAL, "You have no nickname in this room.");
2865   } else {
2866     gchar *nick = to_utf8(arg);
2867     strip_arg_special_chars(nick);
2868     xmpp_room_join(buddy_getjid(bud), nick, NULL);
2869     g_free(nick);
2870   }
2871 }
2872 
room_privmsg(gpointer bud,char * arg)2873 static void room_privmsg(gpointer bud, char *arg)
2874 {
2875   char **paramlst;
2876   gchar *fjid_utf8, *nick, *nick_utf8, *msg;
2877 
2878   paramlst = split_arg(arg, 2, 1); // nickname, message
2879   nick = *paramlst;
2880   arg = *(paramlst+1);
2881 
2882   if (!nick || !*nick || !arg || !*arg) {
2883     scr_LogPrint(LPRINT_NORMAL,
2884                  "Please specify both a Jabber ID and a message.");
2885     free_arg_lst(paramlst);
2886     return;
2887   }
2888 
2889   nick_utf8 = to_utf8(nick);
2890   fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
2891   g_free(nick_utf8);
2892   msg = to_utf8(arg);
2893   send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
2894   g_free(fjid_utf8);
2895   g_free(msg);
2896   free_arg_lst(paramlst);
2897 }
2898 
room_remove(gpointer bud,char * arg)2899 static void room_remove(gpointer bud, char *arg)
2900 {
2901   if (*arg) {
2902     scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
2903                  "the currently-selected room will be removed.");
2904     return;
2905   }
2906 
2907   // Quick check: if there are resources, we haven't left
2908   if (buddy_getinsideroom(bud)) {
2909     scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
2910     return;
2911   }
2912   // Delete the room
2913   roster_del_user(buddy_getjid(bud));
2914   scr_update_buddy_window();
2915   scr_update_roster();
2916 }
2917 
room_topic(gpointer bud,char * arg)2918 static void room_topic(gpointer bud, char *arg)
2919 {
2920   if (!buddy_getinsideroom(bud)) {
2921     scr_LogPrint(LPRINT_NORMAL, "You are not in this room.");
2922     return;
2923   }
2924 
2925   // If no parameter is given, display the current topic
2926   if (!*arg) {
2927     const char *topic = buddy_gettopic(bud);
2928     if (topic)
2929       scr_LogPrint(LPRINT_NORMAL, "Topic: %s", topic);
2930     else
2931       scr_LogPrint(LPRINT_NORMAL, "No topic has been set.");
2932     return;
2933   }
2934 
2935   // If arg is "-", let's clear the topic
2936   if (!g_strcmp0(arg, "-"))
2937     arg = NULL;
2938 
2939   arg = to_utf8(arg);
2940   // If arg is not NULL & option is set, unescape it
2941   if (arg) {
2942     char *unescaped_topic = NULL;
2943 
2944     if (!strncmp(arg, "-u ", 3)) {
2945       char *tmp = arg;
2946       arg = g_strdup(arg + 3);
2947       g_free(tmp);
2948       unescaped_topic = ut_unescape_tabs_cr(arg);
2949 
2950       // We must not free() if the original string was returned
2951       if (unescaped_topic == arg) {
2952         unescaped_topic = NULL;
2953       } else if (unescaped_topic != NULL) {
2954         g_free(arg);
2955         arg = unescaped_topic;
2956       }
2957     }
2958   }
2959 
2960   // Set the topic
2961   xmpp_send_msg(buddy_getjid(bud), NULL, ROSTER_TYPE_ROOM, arg ? arg : "",
2962                 FALSE, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
2963   g_free(arg);
2964 }
2965 
room_destroy(gpointer bud,char * arg)2966 static void room_destroy(gpointer bud, char *arg)
2967 {
2968   gchar *msg;
2969 
2970   if (arg && *arg)
2971     msg = to_utf8(arg);
2972   else
2973     msg = NULL;
2974 
2975   xmpp_room_destroy(buddy_getjid(bud), NULL, msg);
2976   g_free(msg);
2977 }
2978 
room_unlock(gpointer bud,char * arg)2979 static void room_unlock(gpointer bud, char *arg)
2980 {
2981   if (*arg) {
2982     scr_LogPrint(LPRINT_NORMAL, "Unknown parameter.");
2983     return;
2984   }
2985 
2986   xmpp_room_unlock(buddy_getjid(bud));
2987 }
2988 
room_setopt(gpointer bud,char * arg)2989 static void room_setopt(gpointer bud, char *arg)
2990 {
2991   char **paramlst;
2992   char *param, *value;
2993   enum { opt_none = 0, opt_printstatus,
2994          opt_autowhois, opt_flagjoins } option = 0;
2995   guint changed = 0;
2996 
2997   paramlst = split_arg(arg, 2, 1); // param, value
2998   param = *paramlst;
2999   value = *(paramlst+1);
3000   if (!param) {
3001     scr_LogPrint(LPRINT_NORMAL, "Please specify a room option.");
3002     free_arg_lst(paramlst);
3003     return;
3004   }
3005 
3006   if (!strcasecmp(param, "print_status")) {
3007     option = opt_printstatus;
3008   } else if (!strcasecmp(param, "auto_whois")) {
3009     option = opt_autowhois;
3010   } else if (!strcasecmp(param, "flag_joins")) {
3011     option = opt_flagjoins;
3012   } else {
3013     scr_LogPrint(LPRINT_NORMAL, "Wrong option!");
3014     free_arg_lst(paramlst);
3015     return;
3016   }
3017 
3018   // If no value is given, display the current value
3019   if (!value) {
3020     const char *strval;
3021     if (option == opt_printstatus)
3022       strval = strprintstatus[buddy_getprintstatus(bud)];
3023     else if (option == opt_autowhois)
3024       strval = strautowhois[buddy_getautowhois(bud)];
3025     else
3026       strval = strflagjoins[buddy_getflagjoins(bud)];
3027     scr_LogPrint(LPRINT_NORMAL, "%s is set to: %s", param, strval);
3028     free_arg_lst(paramlst);
3029     return;
3030   }
3031 
3032   if (option == opt_printstatus) {
3033     enum room_printstatus eval;
3034     if (!strcasecmp(value, "none")) {
3035       eval = status_none;
3036     } else if (!strcasecmp(value, "in_and_out")) {
3037       eval = status_in_and_out;
3038     } else if (!strcasecmp(value, "all")) {
3039       eval = status_all;
3040     } else {
3041       eval = status_default;
3042       if (strcasecmp(value, "default") != 0)
3043         scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
3044     }
3045     if (eval != buddy_getprintstatus(bud)) {
3046       buddy_setprintstatus(bud, eval);
3047       changed = 1;
3048     }
3049   } else if (option == opt_autowhois) {
3050     enum room_autowhois eval;
3051     if (!strcasecmp(value, "on")) {
3052       eval = autowhois_on;
3053     } else if (!strcasecmp(value, "off")) {
3054       eval = autowhois_off;
3055     } else {
3056       eval = autowhois_default;
3057       if (strcasecmp(value, "default") != 0)
3058         scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
3059     }
3060     if (eval != buddy_getautowhois(bud)) {
3061       buddy_setautowhois(bud, eval);
3062       changed = 1;
3063     }
3064   } else if (option == opt_flagjoins) {
3065     enum room_flagjoins eval;
3066     if (!strcasecmp(value, "none")) {
3067       eval = flagjoins_none;
3068     } else if (!strcasecmp(value, "joins")) {
3069       eval = flagjoins_joins;
3070     } else if (!strcasecmp(value, "all")) {
3071       eval = flagjoins_all;
3072     } else {
3073       eval = flagjoins_default;
3074       if (strcasecmp(value, "default") != 0)
3075         scr_LogPrint(LPRINT_NORMAL, "Unrecognized value, assuming default...");
3076     }
3077     if (eval != buddy_getflagjoins(bud)) {
3078       buddy_setflagjoins(bud, eval);
3079       changed = 1;
3080     }
3081   }
3082   if (changed &&
3083       xmpp_is_bookmarked(buddy_getjid(bud)) &&
3084       settings_opt_get_int("muc_bookmark_autoupdate"))
3085     room_bookmark(bud, NULL);
3086 
3087   free_arg_lst(paramlst);
3088 }
3089 
3090 //  cmd_room_whois(..)
3091 // If interactive is TRUE, chatmode can be enabled.
3092 // Please note that usernick is expected in UTF-8 locale iff interactive is
3093 // FALSE (in order to work correctly with auto_whois).
cmd_room_whois(gpointer bud,const char * usernick,guint interactive)3094 void cmd_room_whois(gpointer bud, const char *usernick, guint interactive)
3095 {
3096   char **paramlst = NULL;
3097   gchar *nick, *buffer;
3098   const char *bjid, *realjid;
3099   const char *rst_msg;
3100   gchar rprio;
3101   enum imstatus rstatus;
3102   enum imrole role;
3103   enum imaffiliation affil;
3104   time_t rst_time;
3105   guint msg_flag = HBB_PREFIX_INFO;
3106 
3107   if (interactive) {
3108     paramlst = split_arg(usernick, 1, 0); // nickname
3109     nick = to_utf8(*paramlst);
3110   } else {
3111     nick = g_strdup(usernick);
3112   }
3113 
3114   if (!nick || !*nick) {
3115     scr_LogPrint(LPRINT_NORMAL, "Please specify a nickname.");
3116     if (paramlst)
3117       free_arg_lst(paramlst);
3118     return;
3119   }
3120 
3121   if (interactive) {
3122     // Enter chat mode
3123     scr_set_chatmode(TRUE);
3124     scr_show_buddy_window();
3125   } else {
3126     msg_flag |= HBB_PREFIX_NOFLAG;
3127   }
3128 
3129   bjid = buddy_getjid(bud);
3130   rstatus = buddy_getstatus(bud, nick);
3131 
3132   if (rstatus == offline) {
3133     scr_LogPrint(LPRINT_NORMAL, "No such member: %s", nick);
3134     if (paramlst)
3135       free_arg_lst(paramlst);
3136     g_free(nick);
3137     return;
3138   }
3139 
3140   rst_time = buddy_getstatustime(bud, nick);
3141   rprio   = buddy_getresourceprio(bud, nick);
3142   rst_msg = buddy_getstatusmsg(bud, nick);
3143   if (!rst_msg) rst_msg = "";
3144 
3145   role = buddy_getrole(bud, nick);
3146   affil = buddy_getaffil(bud, nick);
3147   realjid = buddy_getrjid(bud, nick);
3148 
3149   buffer = g_new(char, 4096);
3150 
3151   snprintf(buffer, 4095, "Whois [%s]", nick);
3152   scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag, 0);
3153   snprintf(buffer, 4095, "Status   : [%c] %s", imstatus2char[rstatus],
3154            rst_msg);
3155   scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3156 
3157   if (rst_time) {
3158     char tbuf[128];
3159 
3160     strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time));
3161     snprintf(buffer, 4095, "Timestamp: %s", tbuf);
3162     scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3163   }
3164 
3165   if (realjid) {
3166     snprintf(buffer, 4095, "JID      : <%s>", realjid);
3167     scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3168   }
3169 
3170   snprintf(buffer, 4095, "Role     : %s", strrole[role]);
3171   scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3172   snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
3173   scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3174   snprintf(buffer, 4095, "Priority : %d", rprio);
3175   scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag | HBB_PREFIX_CONT, 0);
3176 
3177   scr_WriteIncomingMessage(bjid, "End of WHOIS", 0, msg_flag, 0);
3178 
3179   g_free(buffer);
3180   g_free(nick);
3181   if (paramlst)
3182     free_arg_lst(paramlst);
3183 }
3184 
room_bookmark(gpointer bud,char * arg)3185 static void room_bookmark(gpointer bud, char *arg)
3186 {
3187   const char *roomid;
3188   const char *name = NULL, *nick = NULL, *passwd = NULL, *group = NULL;
3189   char *tmpnick = NULL;
3190   enum room_autowhois autowhois = 0;
3191   enum room_flagjoins flagjoins = 0;
3192   enum room_printstatus printstatus = 0;
3193   enum { bm_add = 0, bm_del = 1 } action = 0;
3194   int autojoin = 0, autojoin_set = 0;
3195   int nick_set = 0;
3196 
3197   if (arg && *arg) {
3198     // /room bookmark [add|del] [[+|-]autojoin] [-|nick]
3199     char **paramlst;
3200     char **pp;
3201 
3202     paramlst = split_arg(arg, 4, 0); // At most 4 parameters
3203 
3204     for (pp = paramlst; *pp; pp++) {
3205       if (!strcasecmp(*pp, "add"))
3206         action = bm_add;
3207       else if (!strcasecmp(*pp, "del"))
3208         action = bm_del;
3209       else if (!strcasecmp(*pp, "-autojoin")) {
3210         autojoin = 0;
3211         autojoin_set = 1;
3212       } else if (!strcasecmp(*pp, "+autojoin")
3213           || !strcasecmp(*pp, "autojoin")) {
3214         autojoin = 1;
3215         autojoin_set = 1;
3216       } else if (!strcmp(*pp, "-")) {
3217         nick_set = 1;
3218       } else if (nick_set == 0) {
3219         nick_set = 1;
3220         nick = tmpnick = to_utf8 (*pp);
3221       } else if (nick_set == 1) {
3222         passwd = to_utf8(*pp);
3223       }
3224     }
3225     free_arg_lst(paramlst);
3226   }
3227 
3228   roomid = buddy_getjid(bud);
3229 
3230   if (action == bm_add) {
3231     name = buddy_getname(bud);
3232     if (!nick_set) {
3233       nick = buddy_getnickname(bud);
3234       if (!nick) //we are probably bookmarking offline room
3235         nick = xmpp_get_bookmark_nick(roomid);
3236     }
3237     if (!autojoin_set) {
3238       autojoin = xmpp_get_bookmark_autojoin(roomid);
3239     }
3240     printstatus = buddy_getprintstatus(bud);
3241     autowhois   = buddy_getautowhois(bud);
3242     flagjoins   = buddy_getflagjoins(bud);
3243     group       = buddy_getgroupname(bud);
3244   }
3245 
3246   xmpp_set_storage_bookmark(roomid, name, nick, passwd, autojoin,
3247                             printstatus, autowhois, flagjoins, group);
3248   g_free(tmpnick);
3249 }
3250 
display_all_bookmarks(void)3251 static void display_all_bookmarks(void)
3252 {
3253   GSList *bm, *bmp;
3254   GString *sbuf;
3255   struct bookmark *bm_elt;
3256 
3257   bm = xmpp_get_all_storage_bookmarks();
3258 
3259   if (!bm)
3260     return;
3261 
3262   sbuf = g_string_new("");
3263 
3264   scr_WriteIncomingMessage(NULL, "List of MUC bookmarks:",
3265                            0, HBB_PREFIX_INFO, 0);
3266 
3267   for (bmp = bm; bmp; bmp = g_slist_next(bmp)) {
3268     bm_elt = bmp->data;
3269     g_string_printf(sbuf, "%c <%s>",
3270                     (bm_elt->autojoin ? '*' : ' '), bm_elt->roomjid);
3271     if (bm_elt->nick)
3272       g_string_append_printf(sbuf, " (%s)", bm_elt->nick);
3273     if (bm_elt->password) /* replace password for security reasons */
3274       g_string_append_printf(sbuf, " (*****)");
3275     if (bm_elt->name)
3276       g_string_append_printf(sbuf, " %s", bm_elt->name);
3277     g_free(bm_elt->roomjid);
3278     g_free(bm_elt->name);
3279     g_free(bm_elt->nick);
3280     g_free(bm_elt->password);
3281     g_free(bm_elt);
3282     scr_WriteIncomingMessage(NULL, sbuf->str,
3283                              0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
3284   }
3285 
3286   scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
3287   scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
3288                                  ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
3289   g_string_free(sbuf, TRUE);
3290   g_slist_free(bm);
3291 }
3292 
do_module(char * arg)3293 static void do_module(char *arg)
3294 {
3295 #ifdef MODULES_ENABLE
3296   gboolean force = FALSE;
3297   char **args;
3298 
3299   args = split_arg(arg, 2, 0);
3300   if (!args[0] || !strcmp(args[0], "list")) {
3301     module_list_print();
3302   } else {
3303     const gchar *error = NULL;
3304     const gchar *name = args[1];
3305 
3306     if (name && name[0] == '-' && name[1] == 'f') {
3307       force = TRUE;
3308       name +=2;
3309       while (*name && *name == ' ')
3310         ++name;
3311     }
3312 
3313     if (!strcmp(args[0], "load")) {
3314       error = module_load(name, TRUE, force);
3315       if (error)
3316         scr_log_print(LPRINT_LOGNORM, "Module '%s' loading error: %s", name, error);
3317     } else if (!strcmp(args[0], "unload")) {
3318       error = module_unload(name, TRUE, force);
3319       if (error)
3320         scr_log_print(LPRINT_LOGNORM, "Module '%s' unloading error: %s", name, error);
3321     } else if (!strcmp(args[0], "info"))
3322       module_info_print(name);
3323     else
3324       scr_log_print(LPRINT_LOGNORM, "Error: module: Unknown subcommand.");
3325   }
3326   free_arg_lst(args);
3327 #else
3328   scr_log_print(LPRINT_NORMAL,
3329                 "Please recompile mcabber with modules enabled.");
3330 #endif
3331 }
3332 
do_room(char * arg)3333 static void do_room(char *arg)
3334 {
3335   char **paramlst;
3336   char *subcmd;
3337   gpointer bud;
3338 
3339   if (!xmpp_is_online()) {
3340     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
3341     return;
3342   }
3343 
3344   paramlst = split_arg(arg, 2, 1); // subcmd, arg
3345   subcmd = *paramlst;
3346   arg = *(paramlst+1);
3347 
3348   if (!subcmd || !*subcmd) {
3349     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
3350     free_arg_lst(paramlst);
3351     return;
3352   }
3353 
3354   if (current_buddy) {
3355     bud = BUDDATA(current_buddy);
3356   } else {
3357     if (strcasecmp(subcmd, "join")) {
3358       free_arg_lst(paramlst);
3359       return;
3360     }
3361     // "room join" is a special case, we don't need to have a valid
3362     // current_buddy.
3363     bud = NULL;
3364   }
3365 
3366   if (!strcasecmp(subcmd, "join"))  {
3367     if ((arg = check_room_subcommand(arg, FALSE, NULL)) != NULL)
3368       room_join(bud, arg);
3369   } else if (!strcasecmp(subcmd, "invite"))  {
3370     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3371       room_invite(bud, arg);
3372   } else if (!strcasecmp(subcmd, "affil"))  {
3373     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3374       room_affil(bud, arg);
3375   } else if (!strcasecmp(subcmd, "role"))  {
3376     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3377       room_role(bud, arg);
3378   } else if (!strcasecmp(subcmd, "ban"))  {
3379     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3380       room_ban(bud, arg);
3381   } else if (!strcasecmp(subcmd, "unban"))  {
3382     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3383       room_unban(bud, arg);
3384   } else if (!strcasecmp(subcmd, "kick"))  {
3385     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3386       room_kick(bud, arg);
3387   } else if (!strcasecmp(subcmd, "leave"))  {
3388     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3389       cmd_room_leave(bud, arg);
3390   } else if (!strcasecmp(subcmd, "names"))  {
3391     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3392       room_names(bud, arg);
3393   } else if (!strcasecmp(subcmd, "nick"))  {
3394     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3395       room_nick(bud, arg);
3396   } else if (!strcasecmp(subcmd, "privmsg"))  {
3397     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3398       room_privmsg(bud, arg);
3399   } else if (!strcasecmp(subcmd, "remove"))  {
3400     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3401       room_remove(bud, arg);
3402   } else if (!strcasecmp(subcmd, "destroy"))  {
3403     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3404       room_destroy(bud, arg);
3405   } else if (!strcasecmp(subcmd, "unlock"))  {
3406     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3407       room_unlock(bud, arg);
3408   } else if (!strcasecmp(subcmd, "setopt"))  {
3409     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3410       room_setopt(bud, arg);
3411   } else if (!strcasecmp(subcmd, "topic"))  {
3412     if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3413       room_topic(bud, arg);
3414   } else if (!strcasecmp(subcmd, "whois"))  {
3415     if ((arg = check_room_subcommand(arg, TRUE, bud)) != NULL)
3416       cmd_room_whois(bud, arg, TRUE);
3417   } else if (!strcasecmp(subcmd, "bookmark"))  {
3418     if (!arg && !buddy_getjid(BUDDATA(current_buddy)) &&
3419         buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_SPECIAL)
3420       display_all_bookmarks();
3421     else if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
3422       room_bookmark(bud, arg);
3423   } else {
3424     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
3425   }
3426 
3427   free_arg_lst(paramlst);
3428 }
3429 
do_authorization(char * arg)3430 static void do_authorization(char *arg)
3431 {
3432   char **paramlst;
3433   char *subcmd;
3434   char *jid_utf8;
3435 
3436   if (!xmpp_is_online()) {
3437     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
3438     return;
3439   }
3440 
3441   paramlst = split_arg(arg, 2, 0); // subcmd, [jid]
3442   subcmd = *paramlst;
3443   arg = *(paramlst+1);
3444 
3445   if (!subcmd || !*subcmd) {
3446     scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
3447     goto do_authorization_return;
3448   }
3449 
3450   // Use the provided jid, if it looks valid
3451   if (arg) {
3452     if (!*arg) {
3453       // If no jid is provided, we use the current selected buddy
3454       arg = NULL;
3455     } else {
3456       if (check_jid_syntax(arg)) {
3457         scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
3458                      "<%s> is not a valid Jabber ID.", arg);
3459         goto do_authorization_return;
3460       }
3461     }
3462   }
3463 
3464   if (!arg) {       // Use the current selected buddy's jid
3465     gpointer bud;
3466     guint type;
3467 
3468     if (!current_buddy)
3469       goto do_authorization_return;
3470     bud = BUDDATA(current_buddy);
3471 
3472     jid_utf8 = arg  = (char*)buddy_getjid(bud);
3473     type = buddy_gettype(bud);
3474 
3475     if (!(type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT))) {
3476       scr_LogPrint(LPRINT_NORMAL, "Invalid buddy.");
3477       goto do_authorization_return;
3478     }
3479   } else {
3480     jid_utf8 = to_utf8(arg);
3481   }
3482 
3483   if (!strcasecmp(subcmd, "allow"))  {
3484     xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
3485     scr_LogPrint(LPRINT_LOGNORM,
3486                  "Sent presence subscription approval to <%s>.",
3487                  jid_utf8);
3488   } else if (!strcasecmp(subcmd, "cancel"))  {
3489     xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED);
3490     scr_LogPrint(LPRINT_LOGNORM,
3491                  "<%s> will no longer receive your presence updates.",
3492                  jid_utf8);
3493   } else if (!strcasecmp(subcmd, "request"))  {
3494     xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_SUBSCRIBE);
3495     scr_LogPrint(LPRINT_LOGNORM,
3496                  "Sent presence notification request to <%s>.", jid_utf8);
3497   } else if (!strcasecmp(subcmd, "request_unsubscribe"))  {
3498     xmpp_send_s10n(jid_utf8, LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE);
3499     scr_LogPrint(LPRINT_LOGNORM,
3500                  "Sent presence notification unsubscription request to <%s>.",
3501                  jid_utf8);
3502   } else {
3503     scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
3504   }
3505 
3506   // Only free jid_utf8 if it has been allocated, i.e. if != arg.
3507   if (jid_utf8 && jid_utf8 != arg)
3508     g_free(jid_utf8);
3509 do_authorization_return:
3510   free_arg_lst(paramlst);
3511 }
3512 
do_version(char * arg)3513 static void do_version(char *arg)
3514 {
3515   gchar *ver = mcabber_version();
3516   scr_LogPrint(LPRINT_NORMAL, "This is mcabber version %s.", ver);
3517   g_free(ver);
3518 #ifdef MODULES_ENABLE
3519   scr_LogPrint(LPRINT_NORMAL, "Compiled with modules support (API %s:%d-%d).",
3520          MCABBER_BRANCH, MCABBER_API_MIN, MCABBER_API_VERSION);
3521 # ifdef PKGLIB_DIR
3522   scr_LogPrint(LPRINT_NORMAL, " Modules directory: " PKGLIB_DIR);
3523 # endif
3524 #endif
3525 }
3526 
do_request(char * arg)3527 static void do_request(char *arg)
3528 {
3529   char **paramlst;
3530   char *fjid, *type;
3531   enum iqreq_type numtype = iqreq_none;
3532   char *jid_utf8 = NULL;
3533 
3534   paramlst = split_arg(arg, 2, 0); // type, jid
3535   type = *paramlst;
3536   fjid = *(paramlst+1);
3537 
3538   if (type) {
3539     // Quick check...
3540     if (!strcasecmp(type, "version"))
3541       numtype = iqreq_version;
3542     else if (!strcasecmp(type, "time"))
3543       numtype = iqreq_time;
3544     else if (!strcasecmp(type, "last"))
3545       numtype = iqreq_last;
3546     else if (!strcasecmp(type, "ping"))
3547       numtype = iqreq_ping;
3548     else if (!strcasecmp(type, "vcard"))
3549       numtype = iqreq_vcard;
3550   }
3551 
3552   if (!type || !numtype) {
3553     scr_LogPrint(LPRINT_NORMAL,
3554                  "Please specify a query type (version, time...).");
3555     free_arg_lst(paramlst);
3556     return;
3557   }
3558 
3559   if (!xmpp_is_online()) {
3560     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
3561     free_arg_lst(paramlst);
3562     return;
3563   }
3564 
3565   // Allow special jid "" or "." (current buddy)
3566   if (fjid && (!*fjid || !strcmp(fjid, ".")))
3567     fjid = NULL;
3568 
3569   if (fjid) {
3570     // The JID has been specified.  Quick check...
3571     if (check_jid_syntax(fjid)) {
3572       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
3573                    "<%s> is not a valid Jabber ID.", fjid);
3574       fjid = NULL;
3575     } else {
3576       // Convert jid to lowercase
3577       char *p;
3578       for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
3579         *p = tolower(*p);
3580       fjid = jid_utf8 = to_utf8(fjid);
3581     }
3582   } else {
3583     // Add the current buddy
3584     if (current_buddy)
3585       fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
3586     if (!fjid)
3587       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
3588   }
3589 
3590   if (fjid) {
3591     if (iqreq_vcard == numtype) {
3592       // vCards requests are sent to the bare jid, except in MUC rooms
3593       const char *resource_name = jid_get_resource_name(fjid);
3594       if (resource_name) {
3595         char *bare_jid = jidtodisp(fjid);
3596         if (!roster_find(bare_jid, jidsearch, ROSTER_TYPE_ROOM)) {
3597           g_free(jid_utf8);
3598           fjid = jid_utf8 = bare_jid;
3599         } else {
3600           g_free(bare_jid);
3601         }
3602       }
3603     }
3604     xmpp_request(fjid, numtype);
3605   }
3606   g_free(jid_utf8);
3607   free_arg_lst(paramlst);
3608 }
3609 
do_event(char * arg)3610 static void do_event(char *arg)
3611 {
3612   char **paramlst;
3613   char *evid, *subcmd;
3614   int action = -1;
3615 
3616   paramlst = split_arg(arg, 3, 1); // id, subcmd, optional arg
3617   evid = *paramlst;
3618   subcmd = *(paramlst+1);
3619 
3620   if (!evid || !subcmd) {
3621     // Special case: /event list
3622     if (evid && !strcasecmp(evid, "list"))
3623       evs_display_list();
3624     else
3625       scr_LogPrint(LPRINT_NORMAL,
3626                    "Missing parameter.  Usage: /event num action "
3627                    "[event-specific args]");
3628     free_arg_lst(paramlst);
3629     return;
3630   }
3631 
3632   if (!strcasecmp(subcmd, "reject"))
3633     action = EVS_CONTEXT_REJECT;
3634   else if (!strcasecmp(subcmd, "accept"))
3635     action = EVS_CONTEXT_ACCEPT;
3636   else if (!strcasecmp(subcmd, "ignore"))
3637     action = EVS_CONTEXT_CANCEL;
3638 
3639   if (action == -1) {
3640     scr_LogPrint(LPRINT_NORMAL, "Wrong action parameter.");
3641   } else {
3642     GSList *p;
3643     GSList *evidlst;
3644 
3645     if (!strcmp(evid, "*")) {
3646       // Use completion list
3647       evidlst = evs_geteventslist();
3648     } else {
3649       // Let's create a slist with the provided event id
3650       evidlst = g_slist_append(NULL, evid);
3651     }
3652     for (p = evidlst; p; p = g_slist_next(p)) {
3653       if (evs_callback(p->data, action,
3654                        (const char*)*(paramlst+2)) == -1) {
3655         scr_LogPrint(LPRINT_NORMAL, "Event %s not found.",
3656                      (const char *)p->data);
3657       }
3658     }
3659     g_slist_free(evidlst);
3660   }
3661 
3662   free_arg_lst(paramlst);
3663 }
3664 
do_pgp(char * arg)3665 static void do_pgp(char *arg)
3666 {
3667   char **paramlst;
3668   char *fjid, *subcmd, *keyid;
3669   enum {
3670     pgp_none,
3671     pgp_enable,
3672     pgp_disable,
3673     pgp_setkey,
3674     pgp_force,
3675     pgp_info
3676   } op = 0;
3677   int force = FALSE;
3678 
3679   paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
3680   subcmd = *paramlst;
3681   fjid = *(paramlst+1);
3682   keyid = *(paramlst+2);
3683 
3684   if (!subcmd)
3685     fjid = NULL;
3686   if (!fjid)
3687     keyid = NULL;
3688 
3689   if (subcmd) {
3690     if (!strcasecmp(subcmd, "enable"))
3691       op = pgp_enable;
3692     else if (!strcasecmp(subcmd, "disable"))
3693       op = pgp_disable;
3694     else if (!strcasecmp(subcmd, "setkey"))
3695       op = pgp_setkey;
3696     else if ((!strcasecmp(subcmd, "force")) ||
3697              (!strcasecmp(subcmd, "+force"))) {
3698       op = pgp_force;
3699       force = TRUE;
3700     } else if (!strcasecmp(subcmd, "-force"))
3701       op = pgp_force;
3702     else if (!strcasecmp(subcmd, "info"))
3703       op = pgp_info;
3704   }
3705 
3706   if (!op) {
3707     scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
3708     free_arg_lst(paramlst);
3709     return;
3710   }
3711 
3712   // Allow special jid "" or "." (current buddy)
3713   if (fjid && (!*fjid || !strcmp(fjid, ".")))
3714     fjid = NULL;
3715 
3716   if (fjid) {
3717     // The JID has been specified.  Quick check...
3718     if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
3719       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
3720                    "<%s> is not a valid Jabber ID.", fjid);
3721       fjid = NULL;
3722     } else {
3723       // Convert jid to lowercase and strip resource
3724       char *p;
3725       for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
3726         *p = tolower(*p);
3727       if (*p == JID_RESOURCE_SEPARATOR)
3728         *p = '\0';
3729     }
3730   } else {
3731     gpointer bud = NULL;
3732     if (current_buddy)
3733       bud = BUDDATA(current_buddy);
3734     if (bud) {
3735       guint type = buddy_gettype(bud);
3736       if (type & ROSTER_TYPE_USER)  // Is it a user?
3737         fjid = (char*)buddy_getjid(bud);
3738       else
3739         scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
3740     }
3741   }
3742 
3743   if (fjid) { // fjid is actually a bare jid...
3744     guint disabled;
3745     GString *sbuf;
3746     switch (op) {
3747       case pgp_enable:
3748       case pgp_disable:
3749           settings_pgp_setdisabled(fjid, (op == pgp_disable ? TRUE : FALSE));
3750           break;
3751       case pgp_force:
3752           settings_pgp_setforce(fjid, force);
3753           break;
3754       case pgp_setkey:
3755           settings_pgp_setkeyid(fjid, keyid);
3756           break;
3757       case pgp_info:
3758           sbuf = g_string_new("");
3759           if (settings_pgp_getkeyid(fjid)) {
3760             g_string_printf(sbuf, "PGP Encryption key id: %s",
3761                             settings_pgp_getkeyid(fjid));
3762             scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
3763           }
3764           disabled = settings_pgp_getdisabled(fjid);
3765           g_string_printf(sbuf, "PGP encryption is %s",
3766                           (disabled ?  "disabled" : "enabled"));
3767           scr_WriteIncomingMessage(fjid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
3768           if (!disabled && settings_pgp_getforce(fjid)) {
3769             scr_WriteIncomingMessage(fjid,
3770                                      "Encryption enforced (no negotiation)",
3771                                      0, HBB_PREFIX_INFO, 0);
3772           }
3773           g_string_free(sbuf, TRUE);
3774           break;
3775       default:
3776           break;
3777     }
3778   } else {
3779     scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
3780   }
3781 
3782   free_arg_lst(paramlst);
3783 }
3784 
do_otr(char * arg)3785 static void do_otr(char *arg)
3786 {
3787 #ifdef HAVE_LIBOTR
3788   char **paramlst;
3789   char *fjid, *subcmd, *keyid;
3790   enum {
3791     otr_none,
3792     otr_start,
3793     otr_stop,
3794     otr_fpr,
3795     otr_smpq,
3796     otr_smpr,
3797     otr_smpa,
3798     otr_k,
3799     otr_info
3800   } op = 0;
3801 
3802   if (!otr_enabled()) {
3803     scr_LogPrint(LPRINT_LOGNORM,
3804                  "Warning: OTR hasn't been enabled -- command ignored.");
3805     return;
3806   }
3807 
3808   paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
3809   subcmd = *paramlst;
3810   fjid = *(paramlst+1);
3811   keyid = *(paramlst+2);
3812 
3813   if (!subcmd)
3814     fjid = NULL;
3815   if (!fjid)
3816     keyid = NULL;
3817 
3818   if (subcmd) {
3819     if (!strcasecmp(subcmd, "start"))
3820       op = otr_start;
3821     else if (!strcasecmp(subcmd, "stop"))
3822       op = otr_stop;
3823     else if (!strcasecmp(subcmd, "fingerprint"))
3824       op = otr_fpr;
3825     else if (!strcasecmp(subcmd, "smpq"))
3826       op = otr_smpq;
3827     else if (!strcasecmp(subcmd, "smpr"))
3828       op = otr_smpr;
3829     else if (!strcasecmp(subcmd, "smpa"))
3830       op = otr_smpa;
3831     else if (!strcasecmp(subcmd, "key"))
3832       op = otr_k;
3833     else if (!strcasecmp(subcmd, "info"))
3834       op = otr_info;
3835   }
3836 
3837   if (!op) {
3838     scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
3839     free_arg_lst(paramlst);
3840     return;
3841   }
3842 
3843   if (op == otr_k) {
3844     otr_key();
3845   } else {
3846     // Allow special jid "" or "." (current buddy)
3847     if (fjid && (!*fjid || !strcmp(fjid, ".")))
3848       fjid = NULL;
3849 
3850     if (fjid) {
3851       // The JID has been specified.  Quick check...
3852       if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
3853         scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
3854                      "<%s> is not a valid Jabber ID.", fjid);
3855         fjid = NULL;
3856       } else {
3857         // Convert jid to lowercase and strip resource
3858         char *p;
3859         for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
3860           *p = tolower(*p);
3861         if (*p == JID_RESOURCE_SEPARATOR)
3862           *p = '\0';
3863       }
3864     } else {
3865       gpointer bud = NULL;
3866       if (current_buddy)
3867         bud = BUDDATA(current_buddy);
3868       if (bud) {
3869         guint type = buddy_gettype(bud);
3870         if (type & ROSTER_TYPE_USER)  // Is it a user?
3871           fjid = (char*)buddy_getjid(bud);
3872         else
3873           scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
3874       }
3875     }
3876 
3877     if (fjid) { // fjid is actually a bare jid...
3878       switch (op) {
3879         case otr_start:
3880           otr_establish(fjid);          break;
3881         case otr_stop:
3882           otr_disconnect(fjid);         break;
3883         case otr_fpr:
3884           otr_fingerprint(fjid, keyid); break;
3885         case otr_smpq:
3886           otr_smp_query(fjid, keyid);   break;
3887         case otr_smpr:
3888           otr_smp_respond(fjid, keyid); break;
3889         case otr_smpa:
3890           otr_smp_abort(fjid);          break;
3891         case otr_info:
3892           otr_print_info(fjid);         break;
3893         default:
3894           break;
3895       }
3896     } else {
3897       scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
3898     }
3899   }
3900   free_arg_lst(paramlst);
3901 
3902 #else
3903   scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
3904 #endif /* HAVE_LIBOTR */
3905 }
3906 
3907 #ifdef HAVE_LIBOTR
string_for_otrpolicy(enum otr_policy p)3908 static char *string_for_otrpolicy(enum otr_policy p)
3909 {
3910   switch (p) {
3911     case plain:         return "plain";
3912     case opportunistic: return "opportunistic";
3913     case manual:        return "manual";
3914     case always:        return "always";
3915     default:            return "unknown";
3916   }
3917 }
3918 
dump_otrpolicy(char * k,char * v,void * nothing)3919 static void dump_otrpolicy(char *k, char *v, void *nothing)
3920 {
3921   scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "otrpolicy for %s: %s", k,
3922                string_for_otrpolicy(*(enum otr_policy*)v));
3923 }
3924 #endif
3925 
do_otrpolicy(char * arg)3926 static void do_otrpolicy(char *arg)
3927 {
3928 #ifdef HAVE_LIBOTR
3929   char **paramlst;
3930   char *fjid, *policy;
3931   enum otr_policy p;
3932 
3933   paramlst = split_arg(arg, 2, 0); // [jid|default] policy
3934   fjid = *paramlst;
3935   policy = *(paramlst+1);
3936 
3937   if (!fjid && !policy) {
3938     scr_LogPrint(LPRINT_NORMAL, "default otrpolicy: %s",
3939                  string_for_otrpolicy(settings_otr_getpolicy(NULL)));
3940     settings_foreach(SETTINGS_TYPE_OTR, &dump_otrpolicy, NULL);
3941     free_arg_lst(paramlst);
3942     return;
3943   }
3944 
3945   if (!policy) {
3946     scr_LogPrint(LPRINT_NORMAL,
3947                  "Please call otrpolicy correctly: /otrpolicy (default|jid) "
3948                  "(plain|manual|opportunistic|always)");
3949     free_arg_lst(paramlst);
3950     return;
3951   }
3952 
3953   if (!strcasecmp(policy, "plain")) {
3954     p = plain;
3955   } else if (!strcasecmp(policy, "manual")) {
3956     p = manual;
3957   } else if (!strcasecmp(policy, "opportunistic")) {
3958     p = opportunistic;
3959   } else if (!strcasecmp(policy, "always")) {
3960     p = always;
3961   } else {
3962     /* Fail, we don't know _this_ policy*/
3963     scr_LogPrint(LPRINT_NORMAL, "mcabber doesn't support _this_ policy!");
3964     free_arg_lst(paramlst);
3965     return;
3966   }
3967 
3968   if (!strcasecmp(fjid, "default") || !strcasecmp(fjid, "*")) {
3969     /*set default policy*/
3970     settings_otr_setpolicy(NULL, p);
3971     free_arg_lst(paramlst);
3972     return;
3973   }
3974   // Allow special jid "" or "." (current buddy)
3975   if (fjid && (!*fjid || !strcmp(fjid, ".")))
3976     fjid = NULL;
3977 
3978   if (fjid) {
3979     // The JID has been specified.  Quick check...
3980     if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
3981       scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
3982                    "<%s> is not a valid Jabber ID.", fjid);
3983       fjid = NULL;
3984     } else {
3985       // Convert jid to lowercase and strip resource
3986       char *p;
3987       for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
3988         *p = tolower(*p);
3989       if (*p == JID_RESOURCE_SEPARATOR)
3990         *p = '\0';
3991     }
3992   } else {
3993     gpointer bud = NULL;
3994     if (current_buddy)
3995       bud = BUDDATA(current_buddy);
3996     if (bud) {
3997       guint type = buddy_gettype(bud);
3998       if (type & ROSTER_TYPE_USER)  // Is it a user?
3999         fjid = (char*)buddy_getjid(bud);
4000       else
4001         scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
4002     }
4003   }
4004 
4005   if (fjid)
4006     settings_otr_setpolicy(fjid, p);
4007   else
4008     scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
4009 
4010   free_arg_lst(paramlst);
4011 #else
4012   scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
4013 #endif /* HAVE_LIBOTR */
4014 }
4015 
4016 /* !!!
4017   After changing the /iline arguments names here, you must change ones
4018   in init_bindings().
4019 */
do_iline(char * arg)4020 static void do_iline(char *arg)
4021 {
4022   if (!strcasecmp(arg, "fword")) {
4023     readline_forward_word();
4024   } else if (!strcasecmp(arg, "bword")) {
4025     readline_backward_word();
4026   } else if (!strcasecmp(arg, "word_fdel")) {
4027     readline_forward_kill_word();
4028   } else if (!strcasecmp(arg, "word_bdel")) {
4029     readline_backward_kill_word();
4030   } else if (!strcasecmp(arg, "word_upcase")) {
4031     readline_updowncase_word(1);
4032   } else if (!strcasecmp(arg, "word_downcase")) {
4033     readline_updowncase_word(0);
4034   } else if (!strcasecmp(arg, "word_capit")) {
4035     readline_capitalize_word();
4036   } else if (!strcasecmp(arg, "fchar")) {
4037     readline_forward_char();
4038   } else if (!strcasecmp(arg, "bchar")) {
4039     readline_backward_char();
4040   } else if (!strcasecmp(arg, "char_fdel")) {
4041     readline_forward_kill_char();
4042   } else if (!strcasecmp(arg, "char_bdel")) {
4043     readline_backward_kill_char();
4044   } else if (!strcasecmp(arg, "char_swap")) {
4045     readline_transpose_chars();
4046   } else if (!strcasecmp(arg, "hist_beginning_search_bwd")) {
4047     readline_hist_beginning_search_bwd();
4048   } else if (!strcasecmp(arg, "hist_beginning_search_fwd")) {
4049     readline_hist_beginning_search_fwd();
4050   } else if (!strcasecmp(arg, "hist_prev")) {
4051     readline_hist_prev();
4052   } else if (!strcasecmp(arg, "hist_next")) {
4053     readline_hist_next();
4054   } else if (!strcasecmp(arg, "iline_start")) {
4055     readline_iline_start();
4056   } else if (!strcasecmp(arg, "iline_end")) {
4057     readline_iline_end();
4058   } else if (!strcasecmp(arg, "iline_fdel")) {
4059     readline_forward_kill_iline();
4060   } else if (!strcasecmp(arg, "iline_bdel")) {
4061     readline_backward_kill_iline();
4062   } else if (!strcasecmp(arg, "send_multiline")) {
4063     readline_send_multiline();
4064   } else if (!strcasecmp(arg, "iline_accept")) {
4065     readline_accept_line(FALSE);
4066   } else if (!strcasecmp(arg, "iline_accept_down_hist")) {
4067     readline_accept_line(TRUE);
4068   } else if (!strcasecmp(arg, "compl_cancel")) {
4069     readline_cancel_completion();
4070   } else if (!strcasecmp(arg, "compl_do_fwd")) {
4071     readline_do_completion(TRUE);
4072   } else if (!strcasecmp(arg, "compl_do_bwd")) {
4073     readline_do_completion(FALSE);
4074   } else if (!strcasecmp(arg, "clear_history")) {
4075     readline_clear_history();
4076   } else {
4077     char **paramlst;
4078     char *subcmd;
4079 
4080     paramlst = split_arg(arg, 2, 0); // subcmd, arg
4081     subcmd = *paramlst;
4082     arg = *(paramlst+1);
4083 
4084     if (!subcmd || !*subcmd) {
4085       scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
4086       free_arg_lst(paramlst);
4087       return;
4088     }
4089 
4090     if (!strcasecmp(subcmd, "iline_insert")) {
4091       readline_insert(arg);
4092     } else {
4093       scr_LogPrint(LPRINT_NORMAL, "Invalid subcommand.");
4094     }
4095 
4096     free_arg_lst(paramlst);
4097   }
4098 }
4099 
do_screen_refresh(char * arg)4100 static void do_screen_refresh(char *arg)
4101 {
4102   readline_refresh_screen();
4103 }
4104 
do_chat_disable(char * arg)4105 static void do_chat_disable(char *arg)
4106 {
4107   guint show_roster;
4108 
4109   if (arg && !strcasecmp(arg, "--show-roster"))
4110     show_roster = 1;
4111   else
4112     show_roster = 0;
4113 
4114   readline_disable_chat_mode(show_roster);
4115 }
4116 
source_print_error(const char * path,int eerrno)4117 static int source_print_error(const char *path, int eerrno)
4118 {
4119   scr_LogPrint(LPRINT_DEBUG, "Source: glob (%s) error: %s.",
4120                path, strerror(eerrno));
4121   return 0;
4122 }
4123 
do_source(char * arg)4124 static void do_source(char *arg)
4125 {
4126   static int recur_level;
4127   gchar *filename, *expfname;
4128   glob_t flist;
4129   if (!*arg) {
4130     scr_LogPrint(LPRINT_NORMAL, "Missing filename.");
4131     return;
4132   }
4133   if (recur_level > 20) {
4134     scr_LogPrint(LPRINT_LOGNORM, "** Too many source commands!");
4135     return;
4136   }
4137   filename = g_strdup(arg);
4138   strip_arg_special_chars(filename);
4139   expfname = expand_filename(filename);
4140   g_free(filename);
4141   // match
4142   flist.gl_offs = 0;
4143   if (glob(expfname, 0, source_print_error, &flist)) {
4144     scr_LogPrint(LPRINT_LOGNORM, "Source: error: %s.", strerror (errno));
4145   } else {
4146     unsigned int i;
4147     // sort list
4148     for (i = 1; i < flist.gl_pathc; ++i) {
4149       int j;
4150       for (j = i-1; j > 0; --j) {
4151         char *a = flist.gl_pathv[j+1];
4152         char *b = flist.gl_pathv[j];
4153         if (strcmp(a, b) < 0) {
4154           flist.gl_pathv[j]   = a;
4155           flist.gl_pathv[j+1] = b;
4156         } else {
4157           break;
4158         }
4159       }
4160     }
4161     // source files in list
4162     for (i=0; i < flist.gl_pathc; ++i) {
4163       recur_level++;
4164       cfg_read_file(flist.gl_pathv[i], FALSE);
4165       recur_level--;
4166     }
4167     // free
4168     globfree(&flist);
4169   }
4170   g_free(expfname);
4171 }
4172 
do_connect(char * arg)4173 static void do_connect(char *arg)
4174 {
4175   xmpp_connect();
4176 }
4177 
do_disconnect(char * arg)4178 static void do_disconnect(char *arg)
4179 {
4180   xmpp_disconnect();
4181 }
4182 
do_help(char * arg)4183 static void do_help(char *arg)
4184 {
4185   help_process(arg);
4186 }
4187 
do_echo(char * arg)4188 static void do_echo(char *arg)
4189 {
4190   if (arg)
4191     scr_print_logwindow(arg);
4192 }
4193 
do_carbons(char * arg)4194 static void do_carbons(char *arg)
4195 {
4196   if (!strcasecmp(arg, "info") || !*arg) {
4197     carbons_info();
4198   } else if (!strcasecmp(arg, "enable")) {
4199     carbons_enable();
4200   } else if (!strcasecmp(arg, "disable")) {
4201     carbons_disable();
4202   } else {
4203       scr_log_print(LPRINT_NORMAL, "Unrecognized parameter!");
4204   }
4205 }
4206 
4207 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
4208