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(¬e->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(¬e->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