You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4120 lines
117 KiB
4120 lines
117 KiB
/* |
|
* commands.c -- user commands handling |
|
* |
|
* Copyright (C) 2005-2014 Mikael Berthe <mikael@lilotux.net> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or (at |
|
* your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, but |
|
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
* General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
#include <glob.h> |
|
|
|
#include "commands.h" |
|
#include "help.h" |
|
#include "roster.h" |
|
#include "screen.h" |
|
#include "compl.h" |
|
#include "hooks.h" |
|
#include "hbuf.h" |
|
#include "utils.h" |
|
#include "settings.h" |
|
#include "events.h" |
|
#include "otr.h" |
|
#include "carbons.h" |
|
#include "utf8.h" |
|
#include "xmpp.h" |
|
#include "main.h" |
|
|
|
#define IMSTATUS_AWAY "away" |
|
#define IMSTATUS_ONLINE "online" |
|
#define IMSTATUS_OFFLINE "offline" |
|
#define IMSTATUS_FREE4CHAT "free" |
|
#define IMSTATUS_AVAILABLE "avail" |
|
#define IMSTATUS_NOTAVAILABLE "notavail" |
|
#define IMSTATUS_DONOTDISTURB "dnd" |
|
#ifdef WITH_DEPRECATED_STATUS_INVISIBLE |
|
# define IMSTATUS_INVISIBLE "invisible" |
|
#endif |
|
|
|
// Commands callbacks |
|
static void do_roster(char *arg); |
|
static void do_status(char *arg); |
|
static void do_status_to(char *arg); |
|
static void do_add(char *arg); |
|
static void do_del(char *arg); |
|
static void do_group(char *arg); |
|
static void do_say(char *arg); |
|
static void do_msay(char *arg); |
|
static void do_say_to(char *arg); |
|
static void do_buffer(char *arg); |
|
static void do_clear(char *arg); |
|
static void do_info(char *arg); |
|
static void do_rename(char *arg); |
|
static void do_move(char *arg); |
|
static void do_set(char *arg); |
|
static void do_alias(char *arg); |
|
static void do_bind(char *arg); |
|
static void do_connect(char *arg); |
|
static void do_disconnect(char *arg); |
|
static void do_quit(char *arg); |
|
static void do_rawxml(char *arg); |
|
static void do_room(char *arg); |
|
static void do_authorization(char *arg); |
|
static void do_version(char *arg); |
|
static void do_request(char *arg); |
|
static void do_event(char *arg); |
|
static void do_help(char *arg); |
|
static void do_pgp(char *arg); |
|
static void do_iline(char *arg); |
|
static void do_screen_refresh(char *arg); |
|
static void do_chat_disable(char *arg); |
|
static void do_source(char *arg); |
|
static void do_color(char *arg); |
|
static void do_otr(char *arg); |
|
static void do_otrpolicy(char *arg); |
|
static void do_echo(char *arg); |
|
static void do_carbons(char *arg); |
|
|
|
static void room_bookmark(gpointer bud, char *arg); |
|
|
|
// Global variable for the commands list |
|
static GSList *Commands; |
|
static GSList *safe_commands; |
|
|
|
// cmd_add() |
|
// Adds a command to the commands list and to the CMD completion list |
|
gpointer cmd_add(const char *name, const char *help, guint flags_row1, |
|
guint flags_row2, void (*f)(char*), gpointer userdata) |
|
{ |
|
cmd *n_cmd = g_new0(cmd, 1); |
|
strncpy(n_cmd->name, name, 32-1); |
|
n_cmd->help = help; |
|
n_cmd->completion_flags[0] = flags_row1; |
|
n_cmd->completion_flags[1] = flags_row2; |
|
n_cmd->func = f; |
|
n_cmd->userdata = userdata; |
|
Commands = g_slist_prepend(Commands, n_cmd); |
|
// Add to completion CMD category |
|
compl_add_category_word(COMPL_CMD, name); |
|
return n_cmd; |
|
} |
|
|
|
// cmd_set_safe(name, safe) |
|
// Sets if command can be used in startup configuration file. |
|
gboolean cmd_set_safe(const gchar *name, gboolean safe) |
|
{ |
|
GSList *sel; |
|
if (!name) |
|
return FALSE; |
|
for (sel = safe_commands; sel; sel = sel->next) |
|
if (!strcmp((const char *)sel->data, name)) { |
|
if (safe) { |
|
return FALSE; |
|
} else { |
|
g_free(sel->data); |
|
safe_commands = g_slist_delete_link(safe_commands, sel); |
|
} |
|
} |
|
if (safe) |
|
safe_commands = g_slist_append(safe_commands, g_strdup(name)); |
|
else |
|
return FALSE; |
|
return TRUE; |
|
} |
|
|
|
// cmd_is_safe(name) |
|
// Returns if command is safe or not |
|
gboolean cmd_is_safe(const gchar *name) |
|
{ |
|
GSList *sel; |
|
if (!name) |
|
return FALSE; |
|
for (sel = safe_commands; sel; sel = sel->next) |
|
if (!strcmp((const char *)sel->data, name)) |
|
return TRUE; |
|
return FALSE; |
|
} |
|
|
|
// cmd_init() |
|
// Commands table initialization |
|
// !!! |
|
// After changing commands names and it arguments names here, you must change |
|
// ones in init_bindings()! |
|
// |
|
void cmd_init(void) |
|
{ |
|
cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add, NULL); |
|
cmd_add("alias", "Add an alias", 0, 0, &do_alias, NULL); |
|
cmd_add("authorization", "Manage subscription authorizations", |
|
COMPL_AUTH, COMPL_JID, &do_authorization, NULL); |
|
cmd_add("bind", "Add an key binding", 0, 0, &do_bind, NULL); |
|
cmd_add("buffer", "Manipulate current buddy's buffer (chat window)", |
|
COMPL_BUFFER, 0, &do_buffer, NULL); |
|
cmd_add("carbons", "Manage carbons settings", COMPL_CARBONS, 0, |
|
&do_carbons, NULL); |
|
cmd_add("chat_disable", "Disable chat mode", 0, 0, &do_chat_disable, NULL); |
|
cmd_add("clear", "Clear the dialog window", 0, 0, &do_clear, NULL); |
|
cmd_add("color", "Set coloring options", COMPL_COLOR, 0, &do_color, NULL); |
|
cmd_add("connect", "Connect to the server", 0, 0, &do_connect, NULL); |
|
cmd_add("del", "Delete the current buddy", 0, 0, &do_del, NULL); |
|
cmd_add("disconnect", "Disconnect from server", 0, 0, &do_disconnect, NULL); |
|
cmd_add("echo", "Display a string in the log window", 0, 0, &do_echo, NULL); |
|
cmd_add("event", "Process an event", COMPL_EVENTSID, COMPL_EVENTS, &do_event, |
|
NULL); |
|
cmd_add("group", "Change group display settings", |
|
COMPL_GROUP, COMPL_GROUPNAME, &do_group, NULL); |
|
cmd_add("help", "Display some help", COMPL_CMD, 0, &do_help, NULL); |
|
cmd_add("iline", "Manipulate input buffer", 0, 0, &do_iline, NULL); |
|
cmd_add("info", "Show basic info on current buddy", 0, 0, &do_info, NULL); |
|
cmd_add("move", "Move the current buddy to another group", COMPL_GROUPNAME, |
|
0, &do_move, NULL); |
|
cmd_add("msay", "Send a multi-lines message to the selected buddy", |
|
COMPL_MULTILINE, 0, &do_msay, NULL); |
|
cmd_add("otr", "Manage OTR settings", COMPL_OTR, COMPL_JID, &do_otr, NULL); |
|
cmd_add("otrpolicy", "Manage OTR policies", COMPL_JID, COMPL_OTRPOLICY, |
|
&do_otrpolicy, NULL); |
|
cmd_add("pgp", "Manage PGP settings", COMPL_PGP, COMPL_JID, &do_pgp, NULL); |
|
cmd_add("quit", "Exit the software", 0, 0, &do_quit, NULL); |
|
cmd_add("rawxml", "Send a raw XML string", 0, 0, &do_rawxml, NULL); |
|
cmd_add("rename", "Rename the current buddy", 0, 0, &do_rename, NULL); |
|
cmd_add("request", "Send a Jabber IQ request", COMPL_REQUEST, COMPL_JID, |
|
&do_request, NULL); |
|
cmd_add("room", "MUC actions command", COMPL_ROOM, 0, &do_room, NULL); |
|
cmd_add("roster", "Manipulate the roster/buddylist", COMPL_ROSTER, 0, |
|
&do_roster, NULL); |
|
cmd_add("say", "Say something to the selected buddy", 0, 0, &do_say, NULL); |
|
cmd_add("say_to", "Say something to a specific buddy", COMPL_JID, 0, |
|
&do_say_to, NULL); |
|
cmd_add("screen_refresh", "Redraw mcabber screen", 0, 0, &do_screen_refresh, |
|
NULL); |
|
cmd_add("set", "Set/query an option value", 0, 0, &do_set, NULL); |
|
cmd_add("source", "Read a configuration file", 0, 0, &do_source, NULL); |
|
cmd_add("status", "Show or set your status", COMPL_STATUS, 0, &do_status, |
|
NULL); |
|
cmd_add("status_to", "Show or set your status for one recipient", |
|
COMPL_JID, COMPL_STATUS, &do_status_to, NULL); |
|
cmd_add("version", "Show mcabber version", 0, 0, &do_version, NULL); |
|
|
|
cmd_set_safe("set", TRUE); |
|
cmd_set_safe("bind", TRUE); |
|
cmd_set_safe("alias", TRUE); |
|
cmd_set_safe("pgp", TRUE); |
|
cmd_set_safe("source", TRUE); |
|
cmd_set_safe("status", TRUE); |
|
cmd_set_safe("color", TRUE); |
|
cmd_set_safe("otrpolicy", TRUE); |
|
|
|
// Status category |
|
compl_add_category_word(COMPL_STATUS, "online"); |
|
compl_add_category_word(COMPL_STATUS, "avail"); |
|
#ifdef WITH_DEPRECATED_STATUS_INVISIBLE |
|
compl_add_category_word(COMPL_STATUS, "invisible"); |
|
#endif |
|
compl_add_category_word(COMPL_STATUS, "free"); |
|
compl_add_category_word(COMPL_STATUS, "dnd"); |
|
compl_add_category_word(COMPL_STATUS, "notavail"); |
|
compl_add_category_word(COMPL_STATUS, "away"); |
|
compl_add_category_word(COMPL_STATUS, "offline"); |
|
compl_add_category_word(COMPL_STATUS, "message"); |
|
|
|
// Roster category |
|
compl_add_category_word(COMPL_ROSTER, "bottom"); |
|
compl_add_category_word(COMPL_ROSTER, "top"); |
|
compl_add_category_word(COMPL_ROSTER, "up"); |
|
compl_add_category_word(COMPL_ROSTER, "down"); |
|
compl_add_category_word(COMPL_ROSTER, "group_prev"); |
|
compl_add_category_word(COMPL_ROSTER, "group_next"); |
|
compl_add_category_word(COMPL_ROSTER, "hide"); |
|
compl_add_category_word(COMPL_ROSTER, "show"); |
|
compl_add_category_word(COMPL_ROSTER, "toggle"); |
|
compl_add_category_word(COMPL_ROSTER, "display"); |
|
compl_add_category_word(COMPL_ROSTER, "hide_offline"); |
|
compl_add_category_word(COMPL_ROSTER, "show_offline"); |
|
compl_add_category_word(COMPL_ROSTER, "toggle_offline"); |
|
compl_add_category_word(COMPL_ROSTER, "item_lock"); |
|
compl_add_category_word(COMPL_ROSTER, "item_unlock"); |
|
compl_add_category_word(COMPL_ROSTER, "item_toggle_lock"); |
|
compl_add_category_word(COMPL_ROSTER, "alternate"); |
|
compl_add_category_word(COMPL_ROSTER, "search"); |
|
compl_add_category_word(COMPL_ROSTER, "unread_first"); |
|
compl_add_category_word(COMPL_ROSTER, "unread_next"); |
|
compl_add_category_word(COMPL_ROSTER, "note"); |
|
compl_add_category_word(COMPL_ROSTER, "resource_lock"); |
|
compl_add_category_word(COMPL_ROSTER, "resource_unlock"); |
|
|
|
// Buffer category |
|
compl_add_category_word(COMPL_BUFFER, "clear"); |
|
compl_add_category_word(COMPL_BUFFER, "bottom"); |
|
compl_add_category_word(COMPL_BUFFER, "top"); |
|
compl_add_category_word(COMPL_BUFFER, "up"); |
|
compl_add_category_word(COMPL_BUFFER, "down"); |
|
compl_add_category_word(COMPL_BUFFER, "search_backward"); |
|
compl_add_category_word(COMPL_BUFFER, "search_forward"); |
|
compl_add_category_word(COMPL_BUFFER, "readmark"); |
|
compl_add_category_word(COMPL_BUFFER, "date"); |
|
compl_add_category_word(COMPL_BUFFER, "%"); |
|
compl_add_category_word(COMPL_BUFFER, "purge"); |
|
compl_add_category_word(COMPL_BUFFER, "close"); |
|
compl_add_category_word(COMPL_BUFFER, "close_all"); |
|
compl_add_category_word(COMPL_BUFFER, "scroll_lock"); |
|
compl_add_category_word(COMPL_BUFFER, "scroll_unlock"); |
|
compl_add_category_word(COMPL_BUFFER, "scroll_toggle"); |
|
compl_add_category_word(COMPL_BUFFER, "list"); |
|
compl_add_category_word(COMPL_BUFFER, "save"); |
|
|
|
// Group category |
|
compl_add_category_word(COMPL_GROUP, "fold"); |
|
compl_add_category_word(COMPL_GROUP, "unfold"); |
|
compl_add_category_word(COMPL_GROUP, "toggle"); |
|
|
|
// Multi-line (msay) category |
|
compl_add_category_word(COMPL_MULTILINE, "abort"); |
|
compl_add_category_word(COMPL_MULTILINE, "begin"); |
|
compl_add_category_word(COMPL_MULTILINE, "send"); |
|
compl_add_category_word(COMPL_MULTILINE, "send_to"); |
|
compl_add_category_word(COMPL_MULTILINE, "toggle"); |
|
compl_add_category_word(COMPL_MULTILINE, "toggle_verbatim"); |
|
compl_add_category_word(COMPL_MULTILINE, "verbatim"); |
|
|
|
// Room category |
|
compl_add_category_word(COMPL_ROOM, "affil"); |
|
compl_add_category_word(COMPL_ROOM, "ban"); |
|
compl_add_category_word(COMPL_ROOM, "bookmark"); |
|
compl_add_category_word(COMPL_ROOM, "destroy"); |
|
compl_add_category_word(COMPL_ROOM, "invite"); |
|
compl_add_category_word(COMPL_ROOM, "join"); |
|
compl_add_category_word(COMPL_ROOM, "kick"); |
|
compl_add_category_word(COMPL_ROOM, "leave"); |
|
compl_add_category_word(COMPL_ROOM, "names"); |
|
compl_add_category_word(COMPL_ROOM, "nick"); |
|
compl_add_category_word(COMPL_ROOM, "privmsg"); |
|
compl_add_category_word(COMPL_ROOM, "remove"); |
|
compl_add_category_word(COMPL_ROOM, "role"); |
|
compl_add_category_word(COMPL_ROOM, "setopt"); |
|
compl_add_category_word(COMPL_ROOM, "topic"); |
|
compl_add_category_word(COMPL_ROOM, "unban"); |
|
compl_add_category_word(COMPL_ROOM, "unlock"); |
|
compl_add_category_word(COMPL_ROOM, "whois"); |
|
|
|
// Authorization category |
|
compl_add_category_word(COMPL_AUTH, "allow"); |
|
compl_add_category_word(COMPL_AUTH, "cancel"); |
|
compl_add_category_word(COMPL_AUTH, "request"); |
|
compl_add_category_word(COMPL_AUTH, "request_unsubscribe"); |
|
|
|
// Request (query) category |
|
compl_add_category_word(COMPL_REQUEST, "last"); |
|
compl_add_category_word(COMPL_REQUEST, "ping"); |
|
compl_add_category_word(COMPL_REQUEST, "time"); |
|
compl_add_category_word(COMPL_REQUEST, "vcard"); |
|
compl_add_category_word(COMPL_REQUEST, "version"); |
|
|
|
// Events category |
|
compl_add_category_word(COMPL_EVENTS, "accept"); |
|
compl_add_category_word(COMPL_EVENTS, "ignore"); |
|
compl_add_category_word(COMPL_EVENTS, "reject"); |
|
|
|
// PGP category |
|
compl_add_category_word(COMPL_PGP, "disable"); |
|
compl_add_category_word(COMPL_PGP, "enable"); |
|
compl_add_category_word(COMPL_PGP, "force"); |
|
compl_add_category_word(COMPL_PGP, "info"); |
|
compl_add_category_word(COMPL_PGP, "setkey"); |
|
|
|
// OTR category |
|
compl_add_category_word(COMPL_OTR, "start"); |
|
compl_add_category_word(COMPL_OTR, "stop"); |
|
compl_add_category_word(COMPL_OTR, "fingerprint"); |
|
compl_add_category_word(COMPL_OTR, "smpq"); |
|
compl_add_category_word(COMPL_OTR, "smpr"); |
|
compl_add_category_word(COMPL_OTR, "smpa"); |
|
compl_add_category_word(COMPL_OTR, "info"); |
|
compl_add_category_word(COMPL_OTR, "key"); |
|
|
|
// OTR Policy category |
|
compl_add_category_word(COMPL_OTRPOLICY, "plain"); |
|
compl_add_category_word(COMPL_OTRPOLICY, "manual"); |
|
compl_add_category_word(COMPL_OTRPOLICY, "opportunistic"); |
|
compl_add_category_word(COMPL_OTRPOLICY, "always"); |
|
|
|
// Color category |
|
compl_add_category_word(COMPL_COLOR, "roster"); |
|
compl_add_category_word(COMPL_COLOR, "muc"); |
|
compl_add_category_word(COMPL_COLOR, "mucnick"); |
|
|
|
// Carbons category |
|
compl_add_category_word(COMPL_CARBONS, "info"); |
|
compl_add_category_word(COMPL_CARBONS, "enable"); |
|
compl_add_category_word(COMPL_CARBONS, "disable"); |
|
} |
|
|
|
// expandalias(line) |
|
// If there is one, expand the alias in line and returns a new allocated line |
|
// If no alias is found, returns line |
|
// Note: if the returned pointer is different from line, the caller should |
|
// g_free() the pointer after use |
|
char *expandalias(const char *line) |
|
{ |
|
const char *p1, *p2; |
|
char *word; |
|
const gchar *value; |
|
char *newline = (char*)line; |
|
|
|
// Ignore leading COMMAND_CHAR |
|
for (p1 = line ; *p1 == COMMAND_CHAR ; p1++) |
|
; |
|
// Locate the end of the word |
|
for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++) |
|
; |
|
// Extract the word and look for an alias in the list |
|
word = g_strndup(p1, p2-p1); |
|
value = settings_get(SETTINGS_TYPE_ALIAS, (const char*)word); |
|
g_free(word); |
|
|
|
if (value) |
|
newline = g_strdup_printf("%c%s%s", COMMAND_CHAR, value, p2); |
|
|
|
return newline; |
|
} |
|
|
|
// cmd_get |
|
// Finds command in the command list structure. |
|
// Returns a pointer to the cmd entry, or NULL if command not found. |
|
cmd *cmd_get(const char *command) |
|
{ |
|
const char *p1, *p2; |
|
char *com; |
|
GSList *sl_com; |
|
|
|
// Ignore leading COMMAND_CHAR |
|
for (p1 = command ; *p1 == COMMAND_CHAR ; p1++) |
|
; |
|
// Locate the end of the command |
|
for (p2 = p1 ; *p2 && (*p2 != ' ') ; p2++) |
|
; |
|
// Copy the clean command |
|
com = g_strndup(p1, p2-p1); |
|
|
|
// Look for command in the list |
|
for (sl_com=Commands; sl_com; sl_com = g_slist_next(sl_com)) { |
|
if (!strcasecmp(com, ((cmd*)sl_com->data)->name)) |
|
break; |
|
} |
|
g_free(com); |
|
|
|
if (sl_com) // Command has been found. |
|
return (cmd*)sl_com->data; |
|
return NULL; |
|
} |
|
|
|
// process_command(line, iscmd) |
|
// Process a command line. |
|
// If iscmd is TRUE, process the command even if verbatim mmode is set; |
|
// it is intended to be used for key bindings. |
|
void process_command(const char *line, guint iscmd) |
|
{ |
|
char *p; |
|
char *xpline; |
|
cmd *curcmd; |
|
|
|
if (!line) |
|
return; |
|
|
|
// We do alias expansion here |
|
if (iscmd || scr_get_multimode() != 2) |
|
xpline = expandalias(line); |
|
else |
|
xpline = (char*)line; // No expansion in verbatim multi-line mode |
|
|
|
// We want to use a copy |
|
if (xpline == line) |
|
xpline = g_strdup(line); |
|
|
|
// Remove trailing spaces: |
|
for (p=xpline ; *p ; p++) |
|
; |
|
for (p-- ; p>xpline && (*p == ' ') ; p--) |
|
*p = 0; |
|
|
|
// If verbatim multi-line mode, we check if another /msay command is typed |
|
if (!iscmd && scr_get_multimode() == 2 |
|
&& (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) { |
|
// It isn't an /msay command |
|
scr_append_multiline(xpline); |
|
g_free(xpline); |
|
return; |
|
} |
|
|
|
// Commands handling |
|
curcmd = cmd_get(xpline); |
|
|
|
if (!curcmd) { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized command. " |
|
"Please see the manual for a list of known commands."); |
|
g_free(xpline); |
|
return; |
|
} |
|
if (!curcmd->func) { |
|
scr_LogPrint(LPRINT_NORMAL, |
|
"This functionality is not yet implemented, sorry."); |
|
g_free(xpline); |
|
return; |
|
} |
|
// Lets go to the command parameters |
|
for (p = xpline+1; *p && (*p != ' ') ; p++) |
|
; |
|
// Skip spaces |
|
while (*p && (*p == ' ')) |
|
p++; |
|
// Call command-specific function |
|
(*curcmd->func)(p); |
|
g_free(xpline); |
|
} |
|
|
|
// process_line(line) |
|
// Process a command/message line. |
|
// If this isn't a command, this is a message and it is sent to the |
|
// currently selected buddy. |
|
void process_line(const char *line) |
|
{ |
|
if (!*line) { // User only pressed enter |
|
if (scr_get_multimode()) { |
|
scr_append_multiline(""); |
|
return; |
|
} |
|
if (current_buddy) { |
|
if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP) { |
|
do_group("toggle"); |
|
} else { |
|
// Enter chat mode |
|
scr_set_chatmode(TRUE); |
|
scr_show_buddy_window(); |
|
} |
|
} |
|
return; |
|
} |
|
|
|
if (*line == COMMAND_CHAR) { |
|
if (*(line+1) != COMMAND_CHAR) { |
|
/* It is a command */ |
|
process_command(line, FALSE); |
|
return; |
|
} else if (scr_get_multimode() != 2) { |
|
/* Skip the first COMMAND_CHAR */ |
|
line++; |
|
} |
|
} |
|
|
|
// This isn't a command |
|
if (scr_get_multimode()) |
|
scr_append_multiline(line); |
|
else |
|
say_cmd((char*)line, 0); |
|
} |
|
|
|
// Helper routine for buffer item_{lock,unlock,toggle_lock} |
|
// "lock" values: 1=lock 0=unlock -1=invert |
|
static void roster_buddylock(char *bjid, int lock) |
|
{ |
|
gpointer bud = NULL; |
|
|
|
// Allow special jid "" or "." (current buddy) |
|
if (bjid && (!*bjid || !strcmp(bjid, "."))) |
|
bjid = NULL; |
|
|
|
if (bjid) { |
|
// The JID has been specified. Quick check... |
|
if (check_jid_syntax(bjid)) { |
|
scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, |
|
"<%s> is not a valid Jabber ID.", bjid); |
|
} else { |
|
// Find the buddy |
|
GSList *roster_elt; |
|
roster_elt = roster_find(bjid, jidsearch, |
|
ROSTER_TYPE_USER|ROSTER_TYPE_ROOM); |
|
if (roster_elt) |
|
bud = roster_elt->data; |
|
else |
|
scr_LogPrint(LPRINT_NORMAL, "This jid isn't in the roster."); |
|
} |
|
} else { |
|
// Use the current buddy |
|
if (current_buddy) |
|
bud = BUDDATA(current_buddy); |
|
} |
|
|
|
// Update the ROSTER_FLAG_USRLOCK flag |
|
if (bud) { |
|
if (lock == -1) |
|
lock = !(buddy_getflags(bud) & ROSTER_FLAG_USRLOCK); |
|
buddy_setflags(bud, ROSTER_FLAG_USRLOCK, lock); |
|
buddylist_defer_build(); |
|
scr_update_roster(); |
|
} |
|
} |
|
|
|
static void roster_resourcelock(char *jidres, gboolean lock) { |
|
gpointer bud = NULL; |
|
char *resource = NULL; |
|
|
|
if (!jidres) { |
|
if (lock) return; |
|
jidres = "."; |
|
} |
|
|
|
if (jidres[0] == '.' && |
|
(jidres[1] == '\0' || jidres[1] == JID_RESOURCE_SEPARATOR)) { |
|
//Special jid: . or ./resource |
|
if (current_buddy) |
|
bud = BUDDATA(current_buddy); |
|
if (jidres[1] == JID_RESOURCE_SEPARATOR) |
|
resource = jidres+2; |
|
} else { |
|
if (!check_jid_syntax(jidres) && |
|
jid_get_resource_name(jidres)) { |
|
//Any other valid full jid |
|
char * bare_jid = jidtodisp(jidres); |
|
GSList *roster_elt; |
|
roster_elt = roster_find(bare_jid, jidsearch, |
|
ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); |
|
if (roster_elt) |
|
bud = roster_elt->data; |
|
g_free(bare_jid); |
|
} |
|
if (!bud) { |
|
//Resource for current buddy |
|
if (current_buddy) |
|
bud = BUDDATA(current_buddy); |
|
resource = jidres; |
|
} |
|
} |
|
|
|
if (bud && buddy_gettype(bud) & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) { |
|
if (lock) { |
|
GSList *resources, *p_res; |
|
gboolean found = FALSE; |
|
resources = buddy_getresources(bud); |
|
for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) { |
|
if (!g_strcmp0((char*)p_res->data, resource)) |
|
found = TRUE; |
|
g_free(p_res->data); |
|
} |
|
g_slist_free(resources); |
|
if (!found) { |
|
scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres); |
|
return; |
|
} |
|
} else { |
|
resource = NULL; |
|
} |
|
buddy_setactiveresource(bud, resource); |
|
scr_update_chat_status(TRUE); |
|
} |
|
} |
|
// display_and_free_note(note, winId) |
|
// Display the note information in the winId buffer, and free note |
|
// (winId is a bare jid or NULL for the status window, in which case we |
|
// display the note jid too) |
|
static void display_and_free_note(struct annotation *note, const char *winId) |
|
{ |
|
gchar tbuf[128]; |
|
GString *sbuf; |
|
guint msg_flag = HBB_PREFIX_INFO; |
|
/* We use the flag prefix_info for the first line, and prefix_cont |
|
for the other lines, for better readability */ |
|
|
|
if (!note) |
|
return; |
|
|
|
sbuf = g_string_new(""); |
|
|
|
if (!winId) { |
|
// We're writing to the status window, so let's show the jid too. |
|
g_string_printf(sbuf, "Annotation on <%s>", note->jid); |
|
scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0); |
|
msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT; |
|
} |
|
|
|
// If we have the creation date, display it |
|
if (note->cdate) { |
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", |
|
localtime(¬e->cdate)); |
|
g_string_printf(sbuf, "Note created %s", tbuf); |
|
scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0); |
|
msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT; |
|
} |
|
// If we have the modification date, display it |
|
// unless it's the same as the creation date |
|
if (note->mdate && note->mdate != note->cdate) { |
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", |
|
localtime(¬e->mdate)); |
|
g_string_printf(sbuf, "Note modified %s", tbuf); |
|
scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0); |
|
msg_flag = HBB_PREFIX_INFO | HBB_PREFIX_CONT; |
|
} |
|
// Note text |
|
g_string_printf(sbuf, "Note: %s", note->text); |
|
scr_WriteIncomingMessage(winId, sbuf->str, 0, msg_flag, 0); |
|
|
|
g_string_free(sbuf, TRUE); |
|
g_free(note->text); |
|
g_free(note->jid); |
|
g_free(note); |
|
} |
|
|
|
static void display_all_annotations(void) |
|
{ |
|
GSList *notes; |
|
notes = xmpp_get_all_storage_rosternotes(); |
|
|
|
if (!notes) |
|
return; |
|
|
|
// Call display_and_free_note() for each note, |
|
// with winId = NULL (special window) |
|
g_slist_foreach(notes, (GFunc)&display_and_free_note, NULL); |
|
scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE); |
|
scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE, |
|
ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max); |
|
g_slist_free(notes); |
|
} |
|
|
|
static void roster_note(char *arg) |
|
{ |
|
const char *bjid; |
|
guint type; |
|
|
|
if (!current_buddy) |
|
return; |
|
|
|
bjid = buddy_getjid(BUDDATA(current_buddy)); |
|
type = buddy_gettype(BUDDATA(current_buddy)); |
|
|
|
if (!bjid && type == ROSTER_TYPE_SPECIAL && !arg) { |
|
// We're in the status window (the only special buffer currently) |
|
// Let's display all server notes |
|
display_all_annotations(); |
|
return; |
|
} |
|
|
|
if (!bjid || (type != ROSTER_TYPE_USER && |
|
type != ROSTER_TYPE_ROOM && |
|
type != ROSTER_TYPE_AGENT)) { |
|
scr_LogPrint(LPRINT_NORMAL, "This item can't have a note."); |
|
return; |
|
} |
|
|
|
if (arg && *arg) { // Set a note |
|
gchar *msg, *notetxt; |
|
msg = to_utf8(arg); |
|
if (!strcmp(msg, "-")) |
|
notetxt = NULL; // delete note |
|
else |
|
notetxt = msg; |
|
xmpp_set_storage_rosternotes(bjid, notetxt); |
|
g_free(msg); |
|
} else { // Display a note |
|
struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE); |
|
if (note) { |
|
display_and_free_note(note, bjid); |
|
} else { |
|
scr_WriteIncomingMessage(bjid, "This item doesn't have a note.", 0, |
|
HBB_PREFIX_INFO, 0); |
|
} |
|
} |
|
} |
|
|
|
// roster_updown(updown, nitems) |
|
// updown: -1=up, +1=down |
|
inline static void roster_updown(int updown, char *nitems) |
|
{ |
|
int nbitems; |
|
|
|
if (!nitems || !*nitems) |
|
nbitems = 1; |
|
else |
|
nbitems = strtol(nitems, NULL, 10); |
|
|
|
if (nbitems > 0) |
|
scr_roster_up_down(updown, nbitems); |
|
} |
|
|
|
/* Commands callback functions */ |
|
/* All these do_*() functions will be called with a "arg" parameter */ |
|
/* (with arg not null) */ |
|
|
|
static void do_roster(char *arg) |
|
{ |
|
char **paramlst; |
|
char *subcmd; |
|
|
|
paramlst = split_arg(arg, 2, 1); // subcmd, arg |
|
subcmd = *paramlst; |
|
arg = *(paramlst+1); |
|
|
|
if (!subcmd || !*subcmd) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (!strcasecmp(subcmd, "top")) { |
|
scr_roster_top(); |
|
} else if (!strcasecmp(subcmd, "bottom")) { |
|
scr_roster_bottom(); |
|
} else if (!strcasecmp(subcmd, "hide")) { |
|
scr_roster_visibility(0); |
|
} else if (!strcasecmp(subcmd, "show")) { |
|
scr_roster_visibility(1); |
|
} else if (!strcasecmp(subcmd, "toggle")) { |
|
scr_roster_visibility(-1); |
|
} else if (!strcasecmp(subcmd, "hide_offline")) { |
|
buddylist_set_hide_offline_buddies(TRUE); |
|
scr_update_roster(); |
|
} else if (!strcasecmp(subcmd, "show_offline")) { |
|
buddylist_set_hide_offline_buddies(FALSE); |
|
scr_update_roster(); |
|
} else if (!strcasecmp(subcmd, "toggle_offline")) { |
|
buddylist_set_hide_offline_buddies(-1); |
|
scr_update_roster(); |
|
} else if (!strcasecmp(subcmd, "display")) { |
|
scr_roster_display(arg); |
|
} else if (!strcasecmp(subcmd, "item_lock")) { |
|
roster_buddylock(arg, 1); |
|
} else if (!strcasecmp(subcmd, "item_unlock")) { |
|
roster_buddylock(arg, 0); |
|
} else if (!strcasecmp(subcmd, "item_toggle_lock")) { |
|
roster_buddylock(arg, -1); |
|
} else if (!strcasecmp(subcmd, "unread_first")) { |
|
scr_roster_unread_message(0); |
|
} else if (!strcasecmp(subcmd, "unread_next")) { |
|
scr_roster_unread_message(1); |
|
} else if (!strcasecmp(subcmd, "next_open_buffer")) { |
|
scr_roster_next_open_buffer(); |
|
} else if (!strcasecmp(subcmd, "alternate")) { |
|
scr_roster_jump_alternate(); |
|
} else if (!strncasecmp(subcmd, "search", 6)) { |
|
strip_arg_special_chars(arg); |
|
if (!arg || !*arg) { |
|
scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?"); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
scr_roster_search(arg); |
|
} else if (!strcasecmp(subcmd, "up")) { |
|
roster_updown(-1, arg); |
|
} else if (!strcasecmp(subcmd, "down")) { |
|
roster_updown(1, arg); |
|
} else if (!strcasecmp(subcmd, "group_prev")) { |
|
scr_roster_prev_group(); |
|
} else if (!strcasecmp(subcmd, "group_next")) { |
|
scr_roster_next_group(); |
|
} else if (!strcasecmp(subcmd, "note")) { |
|
roster_note(arg); |
|
} else if (!strcasecmp(subcmd, "resource_lock")) { |
|
roster_resourcelock(arg, TRUE); |
|
} else if (!strcasecmp(subcmd, "resource_unlock")) { |
|
roster_resourcelock(arg, FALSE); |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
} |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
void do_color(char *arg) |
|
{ |
|
char **paramlst; |
|
char *subcmd; |
|
|
|
paramlst = split_arg(arg, 2, 1); // subcmd, arg |
|
subcmd = *paramlst; |
|
arg = *(paramlst+1); |
|
|
|
if (!subcmd || !*subcmd) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (!strcasecmp(subcmd, "roster")) { |
|
char *status, *wildcard, *color; |
|
char **arglist = split_arg(arg, 3, 0); |
|
|
|
status = *arglist; |
|
wildcard = to_utf8(arglist[1]); |
|
color = arglist[2]; |
|
|
|
if (status && !strcmp(status, "clear")) { // Not a color command, clear all |
|
scr_roster_clear_color(); |
|
} else { |
|
if (!status || !*status || !wildcard || !*wildcard || !color || !*color) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing argument"); |
|
} else { |
|
scr_roster_color(status, wildcard, color); |
|
} |
|
} |
|
free_arg_lst(arglist); |
|
g_free(wildcard); |
|
} else if (!strcasecmp(subcmd, "muc")) { |
|
char **arglist = split_arg(arg, 2, 0); |
|
char *free_muc = to_utf8(*arglist); |
|
const char *muc = free_muc, *mode = arglist[1]; |
|
if (!muc || !*muc) { |
|
scr_LogPrint(LPRINT_NORMAL, "What MUC?"); |
|
} else { |
|
if (!strcmp(muc, ".")) |
|
if (!(muc = CURRENT_JID)) |
|
scr_LogPrint(LPRINT_NORMAL, "No JID selected"); |
|
if (muc) { |
|
if (check_jid_syntax(muc) && strcmp(muc, "*")) { |
|
scr_LogPrint(LPRINT_NORMAL, "Not a JID"); |
|
} else { |
|
if (!mode || !*mode || !strcasecmp(mode, "on")) |
|
scr_muc_color(muc, MC_ALL); |
|
else if (!strcasecmp(mode, "preset")) |
|
scr_muc_color(muc, MC_PRESET); |
|
else if (!strcasecmp(mode, "off")) |
|
scr_muc_color(muc, MC_OFF); |
|
else if (!strcmp(mode, "-")) |
|
scr_muc_color(muc, MC_REMOVE); |
|
else |
|
scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode"); |
|
} |
|
} |
|
} |
|
free_arg_lst(arglist); |
|
g_free(free_muc); |
|
} else if (!strcasecmp(subcmd, "mucnick")) { |
|
char **arglist = split_arg(arg, 2, 0); |
|
const char *nick = *arglist, *color = arglist[1]; |
|
if (!nick || !*nick || !color || !*color) |
|
scr_LogPrint(LPRINT_NORMAL, "Missing argument"); |
|
else |
|
scr_muc_nick_color(nick, color); |
|
free_arg_lst(arglist); |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
} |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
// cmd_setstatus(recipient, arg) |
|
// Set your Jabber status. |
|
// - if recipient is not NULL, the status is sent to this contact only |
|
// - arg must be "status message" (message is optional) |
|
void cmd_setstatus(const char *recipient, const char *arg) |
|
{ |
|
char **paramlst; |
|
char *status; |
|
char *msg; |
|
enum imstatus st; |
|
|
|
if (!xmpp_is_online()) |
|
scr_LogPrint(LPRINT_NORMAL, "You are currently not connected..."); |
|
// We do not return now, so that the status is memorized and used later... |
|
|
|
// It makes sense to reset autoaway before changing the status |
|
// (esp. for FIFO or remote commands) or the behaviour could be |
|
// unexpected... |
|
if (!recipient) |
|
scr_check_auto_away(TRUE); |
|
|
|
paramlst = split_arg(arg, 2, 1); // status, message |
|
status = *paramlst; |
|
msg = *(paramlst+1); |
|
|
|
if (!status) { |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (!strcasecmp(status, IMSTATUS_OFFLINE)) st = offline; |
|
else if (!strcasecmp(status, IMSTATUS_ONLINE)) st = available; |
|
else if (!strcasecmp(status, IMSTATUS_AVAILABLE)) st = available; |
|
else if (!strcasecmp(status, IMSTATUS_AWAY)) st = away; |
|
#ifdef WITH_DEPRECATED_STATUS_INVISIBLE |
|
else if (!strcasecmp(status, IMSTATUS_INVISIBLE)) st = invisible; |
|
#endif |
|
else if (!strcasecmp(status, IMSTATUS_DONOTDISTURB)) st = dontdisturb; |
|
else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE)) st = notavail; |
|
else if (!strcasecmp(status, IMSTATUS_FREE4CHAT)) st = freeforchat; |
|
else if (!strcasecmp(status, "message")) { |
|
if (!msg || !*msg) { |
|
// We want a message. If there's none, we give up. |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
st = xmpp_getstatus(); // Preserve current status |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized status!"); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
// Use provided message |
|
if (msg && !*msg) { |
|
msg = NULL; |
|
} |
|
|
|
// If a recipient is specified, let's don't use default status messages |
|
if (recipient && !msg) |
|
msg = ""; |
|
|
|
xmpp_setstatus(st, recipient, msg, FALSE); |
|
|
|
free_arg_lst(paramlst); |
|
} |
|
|
|
static void do_status(char *arg) |
|
{ |
|
if (!*arg) { |
|
const char *sm = xmpp_getstatusmsg(); |
|
scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s", |
|
imstatus2char[xmpp_getstatus()], |
|
(sm ? sm : "")); |
|
return; |
|
} |
|
arg = to_utf8(arg); |
|
cmd_setstatus(NULL, arg); |
|
g_free(arg); |
|
} |
|
|
|
static void do_status_to(char *arg) |
|
{ |
|
char **paramlst; |
|
char *fjid, *st, *msg; |
|
char *jid_utf8 = NULL; |
|
|
|
paramlst = split_arg(arg, 3, 1); // jid, status, [message] |
|
fjid = *paramlst; |
|
st = *(paramlst+1); |
|
msg = *(paramlst+2); |
|
|
|
if (!fjid || !st) { |
|
scr_LogPrint(LPRINT_NORMAL, |
|
"Please specify both a Jabber ID and a status."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
// Allow things like /status_to "" away |
|
if (!*fjid || !strcmp(fjid, ".")) |
|
fjid = NULL; |
|
|
|
if (fjid) { |
|
// The JID has been specified. Quick check... |
|
if (check_jid_syntax(fjid)) { |
|
scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, |
|
"<%s> is not a valid Jabber ID.", fjid); |
|
fjid = NULL; |
|
} else { |
|
// Convert jid to lowercase |
|
char *p = fjid; |
|
for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++) |
|
*p = tolower(*p); |
|
fjid = jid_utf8 = to_utf8(fjid); |
|
} |
|
} else { |
|
// Add the current buddy |
|
if (current_buddy) |
|
fjid = (char*)buddy_getjid(BUDDATA(current_buddy)); |
|
if (!fjid) |
|
scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID."); |
|
} |
|
|
|
if (fjid) { |
|
char *cmdline; |
|
if (!msg) |
|
msg = ""; |
|
msg = to_utf8(msg); |
|
cmdline = g_strdup_printf("%s %s", st, msg); |
|
scr_LogPrint(LPRINT_LOGNORM, "Sending to <%s> /status %s", fjid, cmdline); |
|
cmd_setstatus(fjid, cmdline); |
|
g_free(msg); |
|
g_free(cmdline); |
|
g_free(jid_utf8); |
|
} |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
static void do_add(char *arg) |
|
{ |
|
char **paramlst; |
|
char *id, *nick; |
|
char *jid_utf8 = NULL; |
|
|
|
if (!xmpp_is_online()) { |
|
scr_LogPrint(LPRINT_NORMAL, "You are not connected."); |
|
return; |
|
} |
|
|
|
paramlst = split_arg(arg, 2, 0); // jid, [nickname] |
|
id = *paramlst; |
|
nick = *(paramlst+1); |
|
|
|
if (!id) |
|
nick = NULL; // Allow things like: /add "" nick |
|
else if (!*id || !strcmp(id, ".")) |
|
id = NULL; |
|
|
|
if (id) { |
|
// The JID has been specified. Quick check... |
|
if (check_jid_syntax(id)) { |
|
scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, |
|
"<%s> is not a valid Jabber ID.", id); |
|
id = NULL; |
|
} else { |
|
mc_strtolower(id); |
|
id = jid_utf8 = to_utf8(id); |
|
} |
|
} else { |
|
// Add the current buddy |
|
if (current_buddy) |
|
id = (char*)buddy_getjid(BUDDATA(current_buddy)); |
|
if (!id) |
|
scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID."); |
|
} |
|
|
|
if (nick) |
|
nick = to_utf8(nick); |
|
|
|
if (id) { |
|
// 2nd parameter = optional nickname |
|
xmpp_addbuddy(id, nick, NULL); |
|
scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.", |
|
id); |
|
} |
|
|
|
g_free(jid_utf8); |
|
g_free(nick); |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
static void do_del(char *arg) |
|
{ |
|
const char *bjid; |
|
|
|
if (*arg) { |
|
scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; " |
|
"the currently-selected buddy will be deleted."); |
|
return; |
|
} |
|
|
|
if (!current_buddy) |
|
return; |
|
bjid = buddy_getjid(BUDDATA(current_buddy)); |
|
if (!bjid) |
|
return; |
|
|
|
if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) { |
|
// This is a chatroom |
|
if (buddy_getinsideroom(BUDDATA(current_buddy))) { |
|
scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!"); |
|
return; |
|
} |
|
} |
|
|
|
// Close the buffer |
|
scr_buffer_purge(1, NULL); |
|
|
|
scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid); |
|
xmpp_delbuddy(bjid); |
|
scr_update_buddy_window(); |
|
} |
|
|
|
static void do_group(char *arg) |
|
{ |
|
gpointer group = NULL; |
|
guint leave_buddywindow; |
|
char **paramlst; |
|
char *subcmd; |
|
enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0; |
|
|
|
if (!*arg) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
return; |
|
} |
|
|
|
if (!current_buddy) |
|
return; |
|
|
|
paramlst = split_arg(arg, 2, 0); // subcmd, [arg] |
|
subcmd = *paramlst; |
|
arg = *(paramlst+1); |
|
|
|
if (!subcmd || !*subcmd) |
|
goto do_group_return; // Should not happen |
|
|
|
if (!strcasecmp(subcmd, "expand") || !strcasecmp(subcmd, "unfold")) { |
|
group_state = group_unfold; |
|
} else if (!strcasecmp(subcmd, "shrink") || !strcasecmp(subcmd, "fold")) { |
|
group_state = group_fold; |
|
} else if (!strcasecmp(subcmd, "toggle")) { |
|
group_state = group_toggle; |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
goto do_group_return; |
|
} |
|
|
|
if (arg && *arg) { |
|
GSList *roster_elt; |
|
char *group_utf8 = to_utf8(arg); |
|
roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP); |
|
g_free(group_utf8); |
|
if (roster_elt) |
|
group = buddy_getgroup(roster_elt->data); |
|
} else { |
|
group = buddy_getgroup(BUDDATA(current_buddy)); |
|
} |
|
if (!group) { |
|
scr_LogPrint(LPRINT_NORMAL, "Group not found."); |
|
goto do_group_return; |
|
} |
|
|
|
// We'll have to redraw the chat window if we're not currently on the group |
|
// entry itself, because it means we'll have to leave the current buddy |
|
// chat window. |
|
leave_buddywindow = (group != BUDDATA(current_buddy) && |
|
group == buddy_getgroup(BUDDATA(current_buddy))); |
|
|
|
if (!(buddy_gettype(group) & ROSTER_TYPE_GROUP)) { |
|
scr_LogPrint(LPRINT_NORMAL, "You need to select a group."); |
|
goto do_group_return; |
|
} |
|
|
|
if (group_state != group_unfold && leave_buddywindow) |
|
scr_roster_prev_group(); |
|
|
|
buddy_hide_group(group, group_state); |
|
|
|
buddylist_defer_build(); |
|
scr_update_roster(); |
|
|
|
do_group_return: |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
static int send_message_to(const char *fjid, const char *msg, const char *subj, |
|
LmMessageSubType type_overwrite, bool quiet) |
|
{ |
|
char *bare_jid; |
|
const char *muc_nick; |
|
char *hmsg; |
|
gint crypted; |
|
gint retval = 0; |
|
int isroom; |
|
gpointer xep184 = NULL; |
|
|
|
if (!xmpp_is_online()) { |
|
scr_LogPrint(LPRINT_NORMAL, "You are not connected."); |
|
return 1; |
|
} |
|
if (!fjid || !*fjid) { |
|
scr_LogPrint(LPRINT_NORMAL, "You must specify a Jabber ID."); |
|
return 1; |
|
} |
|
if (!msg || !*msg) { |
|
scr_LogPrint(LPRINT_NORMAL, "You must specify a message."); |
|
return 1; |
|
} |
|
if (check_jid_syntax((char*)fjid)) { |
|
scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, |
|
"<%s> is not a valid Jabber ID.", fjid); |
|
return 1; |
|
} |
|
|
|
// We must use the bare jid in hk_message_out() |
|
bare_jid = jidtodisp(fjid); |
|
|
|
if (!quiet) { |
|
// Jump to window, create one if needed |
|
scr_roster_jump_jid(bare_jid); |
|
} |
|
|
|
// Check if we're sending a message to a conference room |
|
if (NULL != roster_find(bare_jid, jidsearch, ROSTER_TYPE_ROOM)) { |
|
muc_nick = jid_get_resource_name(fjid); |
|
isroom = !muc_nick; // if a resource is specified, then it's a muc private message, not a room |
|
} else { |
|
isroom = false; |
|
muc_nick = NULL; |
|
} |
|
|
|
// local part (UI, logging, etc.) |
|
if (subj) |
|
hmsg = g_strdup_printf("[%s]\n%s", subj, msg); |
|
else |
|
hmsg = (char*)msg; |
|
|
|
// Network part |
|
xmpp_send_msg(fjid, msg, (isroom ? ROSTER_TYPE_ROOM : ROSTER_TYPE_USER), |
|
subj, FALSE, &crypted, type_overwrite, &xep184); |
|
|
|
if (crypted == -1) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Encryption error. Message was not sent."); |
|
retval = 1; |
|
goto send_message_to_return; |
|
} |
|
|
|
// Hook |
|
if (!isroom) |
|
hk_message_out(bare_jid, muc_nick, 0, hmsg, crypted, FALSE, xep184); |
|
|
|
send_message_to_return: |
|
if (hmsg != msg) g_free(hmsg); |
|
g_free(bare_jid); |
|
return retval; |
|
} |
|
|
|
// send_message(msg, subj, type_overwrite) |
|
// Write the message in the buddy's window and send the message on |
|
// the network. |
|
static void send_message(const char *msg, const char *subj, |
|
LmMessageSubType type_overwrite) |
|
{ |
|
const char *bjid; |
|
char *jid; |
|
const char *activeres; |
|
|
|
if (!current_buddy) { |
|
scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected."); |
|
return; |
|
} |
|
|
|
bjid = CURRENT_JID; |
|
if (!bjid) { |
|
scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected."); |
|
return; |
|
} |
|
|
|
activeres = buddy_getactiveresource(BUDDATA(current_buddy)); |
|
if (activeres) |
|
jid = g_strdup_printf("%s/%s", bjid, activeres); |
|
else |
|
jid = g_strdup(bjid); |
|
|
|
send_message_to(jid, msg, subj, type_overwrite, FALSE); |
|
g_free(jid); |
|
} |
|
|
|
static LmMessageSubType scan_mtype(char **arg) |
|
{ |
|
// Try splitting it |
|
char **parlist = split_arg(*arg, 2, 1); |
|
LmMessageSubType result = LM_MESSAGE_SUB_TYPE_NOT_SET; |
|
// Is it a good parameter? |
|
if (parlist && *parlist) { |
|
if (!strcmp("-n", *parlist)) { |
|
result = LM_MESSAGE_SUB_TYPE_NORMAL; |
|
} else if (!strcmp("-h", *parlist)) { |
|
result = LM_MESSAGE_SUB_TYPE_HEADLINE; |
|
} |
|
if (result != LM_MESSAGE_SUB_TYPE_NOT_SET || (!strcmp("--", *parlist))) |
|
*arg += strlen(*arg) - (parlist[1] ? strlen(parlist[1]) : 0); |
|
} |
|
// Anything found? -> skip it |
|
free_arg_lst(parlist); |
|
return result; |
|
} |
|
|
|
void say_cmd(char *arg, int parse_flags) |
|
{ |
|
gpointer bud; |
|
LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET; |
|
|
|
scr_set_chatmode(TRUE); |
|
scr_show_buddy_window(); |
|
|
|
if (!current_buddy) { |
|
scr_LogPrint(LPRINT_NORMAL, |
|
"Whom are you talking to? Please select a buddy."); |
|
return; |
|
} |
|
|
|
bud = BUDDATA(current_buddy); |
|
if (!(buddy_gettype(bud) & |
|
(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) { |
|
scr_LogPrint(LPRINT_NORMAL, "This is not a user."); |
|
return; |
|
} |
|
|
|
buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE); |
|
if (parse_flags) |
|
msgtype = scan_mtype(&arg); |
|
arg = to_utf8(arg); |
|
send_message(arg, NULL, msgtype); |
|
g_free(arg); |
|
} |
|
|
|
static void do_say(char *arg) { |
|
say_cmd(arg, 1); |
|
} |
|
|
|
static void do_msay(char *arg) |
|
{ |
|
/* Parameters: begin verbatim abort send send_to */ |
|
char **paramlst; |
|
char *subcmd; |
|
|
|
paramlst = split_arg(arg, 2, 1); // subcmd, arg |
|
subcmd = *paramlst; |
|
arg = *(paramlst+1); |
|
|
|
if (!subcmd || !*subcmd) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
scr_LogPrint(LPRINT_NORMAL, "Please read the manual before using " |
|
"the /msay command."); |
|
scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter " |
|
"multi-line mode...)", mkcmdstr("msay")); |
|
goto do_msay_return; |
|
} |
|
|
|
if (!strcasecmp(subcmd, "toggle")) { |
|
if (scr_get_multimode()) |
|
subcmd = "send"; |
|
else |
|
subcmd = "begin"; |
|
} else if (!strcasecmp(subcmd, "toggle_verbatim")) { |
|
if (scr_get_multimode()) |
|
subcmd = "send"; |
|
else |
|
subcmd = "verbatim"; |
|
} |
|
|
|
if (!strcasecmp(subcmd, "abort")) { |
|
if (scr_get_multimode()) |
|
scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode."); |
|
scr_set_multimode(FALSE, NULL); |
|
goto do_msay_return; |
|
} else if ((!strcasecmp(subcmd, "begin")) || |
|
(!strcasecmp(subcmd, "verbatim"))) { |
|
bool verbat; |
|
gchar *subj_utf8 = to_utf8(arg); |
|
if (!strcasecmp(subcmd, "verbatim")) { |
|
scr_set_multimode(2, subj_utf8); |
|
verbat = TRUE; |
|
} else { |
|
scr_set_multimode(1, subj_utf8); |
|
verbat = FALSE; |
|
} |
|
|
|
scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.", |
|
verbat ? "VERBATIM " : ""); |
|
scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" " |
|
"when your message is ready.", mkcmdstr("msay")); |
|
if (verbat) |
|
scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.", |
|
mkcmdstr("msay")); |
|
g_free(subj_utf8); |
|
goto do_msay_return; |
|
} else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
goto do_msay_return; |
|
} |
|
|
|
/* send/send_to command */ |
|
|
|
if (!scr_get_multimode()) { |
|
scr_LogPrint(LPRINT_NORMAL, "No message to send. " |
|
"Use \"%s begin\" first.", mkcmdstr("msay")); |
|
goto do_msay_return; |
|
} |
|
|
|
scr_set_chatmode(TRUE); |
|
scr_show_buddy_window(); |
|
|
|
if (!strcasecmp(subcmd, "send_to")) { |
|
int err = FALSE; |
|
gchar *msg_utf8; |
|
LmMessageSubType msg_type = scan_mtype(&arg); |
|
// Let's send to the specified JID. We leave now if there |
|
// has been an error (so we don't leave multi-line mode). |
|
arg = to_utf8(arg); |
|
msg_utf8 = to_utf8(scr_get_multiline()); |
|
if (msg_utf8) { |
|
err = send_message_to(arg, msg_utf8, scr_get_multimode_subj(), msg_type, |
|
FALSE); |
|
g_free(msg_utf8); |
|
} |
|
g_free(arg); |
|
if (err) |
|
goto do_msay_return; |
|
} else { // Send to currently selected buddy |
|
gpointer bud; |
|
gchar *msg_utf8; |
|
|
|
if (!current_buddy) { |
|
scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?"); |
|
goto do_msay_return; |
|
} |
|
|
|
bud = BUDDATA(current_buddy); |
|
if (!(buddy_gettype(bud) & |
|
(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) { |
|
scr_LogPrint(LPRINT_NORMAL, "This is not a user."); |
|
goto do_msay_return; |
|
} |
|
|
|
buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE); |
|
msg_utf8 = to_utf8(scr_get_multiline()); |
|
if (msg_utf8) { |
|
send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg)); |
|
g_free(msg_utf8); |
|
} |
|
} |
|
scr_set_multimode(FALSE, NULL); |
|
scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode."); |
|
do_msay_return: |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
// load_message_from_file(filename) |
|
// Read the whole content of a file. |
|
// The data are converted to UTF8, they should be freed by the caller after |
|
// use. |
|
char *load_message_from_file(const char *filename) |
|
{ |
|
FILE *fd; |
|
struct stat buf; |
|
char *msgbuf, *msgbuf_utf8; |
|
char *p; |
|
gboolean valid; |
|
size_t len; |
|
|
|
fd = fopen(filename, "r"); |
|
|
|
if (!fd || fstat(fileno(fd), &buf)) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename); |
|
return NULL; |
|
} |
|
if (!buf.st_size || buf.st_size >= HBB_BLOCKSIZE) { |
|
if (!buf.st_size) |
|
scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename); |
|
else |
|
scr_LogPrint(LPRINT_LOGNORM, "Message file is too big (%s)", filename); |
|
fclose(fd); |
|
return NULL; |
|
} |
|
|
|
msgbuf = g_new0(char, HBB_BLOCKSIZE); |
|
len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd); |
|
fclose(fd); |
|
|
|
// Check there is no binary data. It must be a *message* file! |
|
valid = TRUE; |
|
if (utf8_mode) { |
|
valid = g_utf8_validate(msgbuf, len, (const gchar **)&p); |
|
} else { // Non-UTF8 |
|
for (p = msgbuf ; *p; p++) { |
|
if (!utf8_mode) { |
|
unsigned char sc = *p; |
|
if (!iswprint(sc) && sc != '\n' && sc != '\t') { |
|
valid = FALSE; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (valid && (*p || p != len+msgbuf)) { |
|
valid = FALSE; // We're not at the End Of Line... |
|
} |
|
if (!valid) { |
|
scr_LogPrint(LPRINT_LOGNORM, "Message file contains " |
|
"invalid characters (%s)", filename); |
|
g_free(msgbuf); |
|
return NULL; |
|
} |
|
|
|
// p is now at the EOL |
|
// Let's strip trailing newlines |
|
if (p > msgbuf) |
|
p--; |
|
while (p > msgbuf && *p == '\n') |
|
*p-- = 0; |
|
|
|
// It could be empty, once the trailing newlines are gone |
|
if (p == msgbuf && *p == '\n') { |
|
scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename); |
|
g_free(msgbuf); |
|
return NULL; |
|
} |
|
|
|
msgbuf_utf8 = to_utf8(msgbuf); |
|
|
|
if (!msgbuf_utf8 && msgbuf) |
|
scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)", |
|
filename); |
|
g_free(msgbuf); |
|
return msgbuf_utf8; |
|
} |
|
|
|
static void do_say_to(char *arg) |
|
{ |
|
char **paramlst; |
|
char *fjid, *msg_utf8; |
|
char *msg; |
|
char *unescaped_msg = NULL; |
|
char *uncompletedfjid = NULL; |
|
char *file = NULL; |
|
LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET; |
|
bool quiet = FALSE; |
|
bool eval = FALSE; |
|
|
|
if (!xmpp_is_online()) { |
|
scr_LogPrint(LPRINT_NORMAL, "You are not connected."); |
|
return; |
|
} |
|
|
|
msg_type = scan_mtype(&arg); |
|
paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message) |
|
|
|
if (!*paramlst) { // No parameter? |
|
scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
// Check for an option parameter |
|
while (*paramlst) { |
|
if (!strcmp(*paramlst, "-q")) { |
|
char **oldparamlst = paramlst; |
|
paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message |
|
free_arg_lst(oldparamlst); |
|
quiet = TRUE; |
|
} else if (!strcmp(*paramlst, "-e")) { |
|
char **oldparamlst = paramlst; |
|
paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message |
|
free_arg_lst(oldparamlst); |
|
eval = TRUE; |
|
} else if (!strcmp(*paramlst, "-f")) { |
|
char **oldparamlst = paramlst; |
|
paramlst = split_arg(*(oldparamlst+1), 2, 1); // filename, jid |
|
free_arg_lst(oldparamlst); |
|
if (!*paramlst) { |
|
scr_LogPrint(LPRINT_NORMAL, "Wrong usage."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
file = g_strdup(*paramlst); |
|
// One more parameter shift... |
|
oldparamlst = paramlst; |
|
paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, nothing |
|
free_arg_lst(oldparamlst); |
|
} else { |
|
break; |
|
} |
|
} |
|
|
|
if (!*paramlst) { |
|
scr_LogPrint(LPRINT_NORMAL, "Wrong usage."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
fjid = *paramlst; |
|
msg = *(paramlst+1); |
|
|
|
if (fjid[0] == '.') { |
|
const gchar *cjid = (current_buddy ? CURRENT_JID : NULL); |
|
if (fjid[1] == '\0') { |
|
fjid = g_strdup(cjid); |
|
} else if (fjid[1] == JID_RESOURCE_SEPARATOR) { |
|
if (!cjid) { |
|
fjid = NULL; |
|
} else { |
|
char *res_utf8 = to_utf8(fjid+2); |
|
fjid = g_strdup_printf("%s%c%s", cjid, JID_RESOURCE_SEPARATOR, res_utf8); |
|
g_free(res_utf8); |
|
} |
|
} else { |
|
fjid = to_utf8(fjid); |
|
} |
|
} else { |
|
fjid = to_utf8(fjid); |
|
} |
|
|
|
if (!fjid) { |
|
scr_LogPrint(LPRINT_NORMAL, "The Jabber ID is invalid."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (!strchr(fjid, JID_DOMAIN_SEPARATOR)) { |
|
const gchar *append_server = settings_opt_get("default_server"); |
|
if (append_server) { |
|
const char *res = jid_get_resource_name(fjid); |
|
uncompletedfjid = jidtodisp(fjid); |
|
g_free(fjid); |
|
if (res) { |
|
fjid = g_strdup_printf("%s%c%s%c%s", uncompletedfjid, JID_DOMAIN_SEPARATOR, |
|
append_server, JID_RESOURCE_SEPARATOR, res); |
|
} else { |
|
fjid = g_strdup_printf("%s%c%s", uncompletedfjid, JID_DOMAIN_SEPARATOR, |
|
append_server); |
|
} |
|
} |
|
} |
|
|
|
if (check_jid_syntax(fjid)) { |
|
scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID."); |
|
free_arg_lst(paramlst); |
|
g_free(uncompletedfjid); |
|
g_free(fjid); |
|
return; |
|
} |
|
|
|
if (!file) { |
|
msg_utf8 = to_utf8(msg); |
|
if (eval) { |
|
unescaped_msg = ut_unescape_tabs_cr(msg_utf8); |
|
// We must not free() if the original string was returned |
|
if (unescaped_msg == msg_utf8) |
|
unescaped_msg = NULL; |
|
} |
|
msg = (unescaped_msg ? unescaped_msg : msg_utf8); |
|
} else { |
|
char *filename_xp; |
|
if (msg) |
|
scr_LogPrint(LPRINT_NORMAL, "say_to: extra parameter ignored."); |
|
filename_xp = expand_filename(file); |
|
msg = msg_utf8 = load_message_from_file(filename_xp); |
|
g_free(filename_xp); |
|
g_free(file); |
|
} |
|
|
|
send_message_to(fjid, msg, NULL, msg_type, quiet); |
|
|
|
g_free(uncompletedfjid); |
|
g_free(fjid); |
|
g_free(msg_utf8); |
|
g_free(unescaped_msg); |
|
free_arg_lst(paramlst); |
|
} |
|
|
|
// buffer_updown(updown, nblines) |
|
// updown: -1=up, +1=down |
|
inline static void buffer_updown(int updown, char *nlines) |
|
{ |
|
int nblines; |
|
|
|
if (!nlines || !*nlines) |
|
nblines = 0; |
|
else |
|
nblines = strtol(nlines, NULL, 10); |
|
|
|
if (nblines >= 0) |
|
scr_buffer_scroll_up_down(updown, nblines); |
|
} |
|
|
|
static void buffer_search(int direction, char *arg) |
|
{ |
|
if (!arg || !*arg) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
return; |
|
} |
|
|
|
scr_buffer_search(direction, arg); |
|
} |
|
|
|
static void buffer_date(char *date) |
|
{ |
|
time_t t; |
|
|
|
if (!date || !*date) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
return; |
|
} |
|
|
|
strip_arg_special_chars(date); |
|
|
|
t = from_iso8601(date, 0); |
|
if (t) |
|
scr_buffer_date(t); |
|
else |
|
scr_LogPrint(LPRINT_NORMAL, "The date you specified is " |
|
"not correctly formatted or invalid."); |
|
} |
|
|
|
static void buffer_percent(char *arg1, char *arg2) |
|
{ |
|
// Basically, user has typed "%arg1 arg2" |
|
// "%50" -> arg1 = 50, arg2 null pointer |
|
// "% 50" -> arg1 = \0, arg2 = 50 |
|
|
|
if (!*arg1 && (!arg2 || !*arg2)) { // No value |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
return; |
|
} |
|
|
|
if (*arg1 && arg2 && *arg2) { // Two values |
|
scr_LogPrint(LPRINT_NORMAL, "Wrong parameters."); |
|
return; |
|
} |
|
|
|
scr_buffer_percent(atoi((*arg1 ? arg1 : arg2))); |
|
} |
|
|
|
static void do_buffer(char *arg) |
|
{ |
|
char **paramlst; |
|
char *subcmd; |
|
|
|
if (!current_buddy) |
|
return; |
|
|
|
paramlst = split_arg(arg, 2, 1); // subcmd, arg |
|
subcmd = *paramlst; |
|
arg = *(paramlst+1); |
|
|
|
if (!subcmd || !*subcmd) { |
|
scr_LogPrint(LPRINT_NORMAL, "Missing parameter."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP && |
|
strcasecmp(subcmd, "close_all")) { |
|
scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer."); |
|
free_arg_lst(paramlst); |
|
return; |
|
} |
|
|
|
if (!strcasecmp(subcmd, "top")) { |
|
scr_buffer_top_bottom(-1); |
|
} else if (!strcasecmp(subcmd, "bottom")) { |
|
scr_buffer_top_bottom(1); |
|
} else if (!strcasecmp(subcmd, "clear")) { |
|
scr_buffer_clear(); |
|
} else if (!strcasecmp(subcmd, "close")) { |
|
scr_buffer_purge(1, arg); |
|
} else if (!strcasecmp(subcmd, "close_all")) { |
|
scr_buffer_purge_all(1); |
|
} else if (!strcasecmp(subcmd, "purge")) { |
|
scr_buffer_purge(0, arg); |
|
} else if (!strcasecmp(subcmd, "scroll_lock")) { |
|
scr_buffer_scroll_lock(1); |
|
} else if (!strcasecmp(subcmd, "scroll_unlock")) { |
|
scr_buffer_scroll_lock(0); |
|
} else if (!strcasecmp(subcmd, "scroll_toggle")) { |
|
scr_buffer_scroll_lock(-1); |
|
} else if (!strcasecmp(subcmd, "up")) { |
|
buffer_updown(-1, arg); |
|
} else if (!strcasecmp(subcmd, "down")) { |
|
buffer_updown(1, arg); |
|
} else if (!strcasecmp(subcmd, "search_backward")) { |
|
strip_arg_special_chars(arg); |
|
buffer_search(-1, arg); |
|
} else if (!strcasecmp(subcmd, "search_forward")) { |
|
strip_arg_special_chars(arg); |
|
buffer_search(1, arg); |
|
} else if (!strcasecmp(subcmd, "date")) { |
|
buffer_date(arg); |
|
} else if (*subcmd == '%') { |
|
buffer_percent(subcmd+1, arg); |
|
} else if (!strcasecmp(subcmd, "save")) { |
|
scr_buffer_dump(arg); |
|
} else if (!strcasecmp(subcmd, "list")) { |
|
scr_buffer_list(); |
|
} else if (!strcasecmp(subcmd, "readmark")) { |
|
scr_buffer_jump_readmark(); |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
} |
|
|
|
free_arg_lst(paramlst); |
|
} |
|
|
|
static void do_clear(char *arg) // Alias for "buffer clear" |
|
{ |
|
do_buffer("clear"); |
|
} |
|
|
|
static void do_info(char *arg) |
|
{ |
|
gpointer bud; |
|
const char *bjid, *name; |
|
guint type, on_srv; |
|
char *buffer; |
|
enum subscr esub; |
|
|
|
if (!current_buddy) |
|
return; |
|
bud = BUDDATA(current_buddy); |
|
|
|
bjid = buddy_getjid(bud); |
|
name = buddy_getname(bud); |
|
type = buddy_gettype(bud); |
|
esub = buddy_getsubscription(bud); |
|
on_srv = buddy_getonserverflag(bud); |
|
|
|
buffer = g_new(char, 4096); |
|
|
|
if (bjid) { |
|
GSList *resources, *p_res; |
|
char *bstr = "unknown"; |
|
|
|
// Enter chat mode |
|
scr_set_chatmode(TRUE); |
|
scr_show_buddy_window(); |
|
|
|
snprintf(buffer, 4095, "jid: <%s>", bjid); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
if (name) { |
|
snprintf(buffer, 4095, "Name: %s", name); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
} |
|
|
|
if (type == ROSTER_TYPE_USER) bstr = "user"; |
|
else if (type == ROSTER_TYPE_ROOM) bstr = "chatroom"; |
|
else if (type == ROSTER_TYPE_AGENT) bstr = "agent"; |
|
snprintf(buffer, 127, "Type: %s", bstr); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
|
|
if (!on_srv) { |
|
scr_WriteIncomingMessage(bjid, "(Local item, not on the server)", |
|
0, HBB_PREFIX_INFO, 0); |
|
} |
|
|
|
if (esub == sub_both) bstr = "both"; |
|
else if (esub & sub_from) bstr = "from"; |
|
else if (esub & sub_to) bstr = "to"; |
|
else bstr = "none"; |
|
snprintf(buffer, 64, "Subscription: %s", bstr); |
|
if (esub & sub_pending) |
|
strcat(buffer, " (pending)"); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
|
|
resources = buddy_getresources(bud); |
|
if (!resources && type == ROSTER_TYPE_USER) { |
|
// No resource; display last status message, if any. |
|
const char *rst_msg = buddy_getstatusmsg(bud, ""); |
|
if (rst_msg) { |
|
snprintf(buffer, 4095, "Last status message: %s", rst_msg); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
} |
|
} |
|
for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) { |
|
gchar rprio; |
|
enum imstatus rstatus; |
|
const char *rst_msg; |
|
time_t rst_time; |
|
|
|
rprio = buddy_getresourceprio(bud, p_res->data); |
|
rstatus = buddy_getstatus(bud, p_res->data); |
|
rst_msg = buddy_getstatusmsg(bud, p_res->data); |
|
rst_time = buddy_getstatustime(bud, p_res->data); |
|
|
|
snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus], |
|
rprio, (char*)p_res->data); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
if (rst_msg) { |
|
snprintf(buffer, 4095, "Status message: %s", rst_msg); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
if (rst_time) { |
|
char tbuf[128]; |
|
|
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&rst_time)); |
|
snprintf(buffer, 4095, "Status timestamp: %s", tbuf); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
#ifdef HAVE_GPGME |
|
struct pgp_data *rpgp = buddy_resource_pgp(bud, p_res->data); |
|
|
|
if (rpgp && rpgp->sign_keyid) { |
|
snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
if (rpgp->last_sigsum) { |
|
gpgme_sigsum_t ss = rpgp->last_sigsum; |
|
snprintf(buffer, 4095, "Last PGP signature: %s", |
|
(ss & GPGME_SIGSUM_GREEN ? "good": |
|
(ss & GPGME_SIGSUM_RED ? "bad" : "unknown"))); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
} |
|
#endif |
|
g_free(p_res->data); |
|
} |
|
g_slist_free(resources); |
|
} else { /* Item has no jid */ |
|
if (name) scr_LogPrint(LPRINT_NORMAL, "Name: %s", name); |
|
scr_LogPrint(LPRINT_NORMAL, "Type: %s", |
|
type == ROSTER_TYPE_GROUP ? "group" : |
|
(type == ROSTER_TYPE_SPECIAL ? "special" : "unknown")); |
|
} |
|
g_free(buffer); |
|
|
|
// Tell the user if this item has an annotation. |
|
if (type == ROSTER_TYPE_USER || |
|
type == ROSTER_TYPE_ROOM || |
|
type == ROSTER_TYPE_AGENT) { |
|
struct annotation *note = xmpp_get_storage_rosternotes(bjid, TRUE); |
|
if (note) { |
|
// We do not display the note, we just tell the user. |
|
g_free(note->text); |
|
g_free(note->jid); |
|
g_free(note); |
|
scr_WriteIncomingMessage(bjid, "(This item has an annotation)", 0, |
|
HBB_PREFIX_INFO, 0); |
|
} |
|
} |
|
} |
|
|
|
// room_names() is a variation of do_info(), for chatrooms only |
|
static void room_names(gpointer bud, char *arg) |
|
{ |
|
const char *bjid; |
|
char *buffer; |
|
GSList *resources, *p_res; |
|
enum { style_normal = 0, style_detail, style_short, |
|
style_quiet, style_compact } style = 0; |
|
int cnt = 0; |
|
|
|
if (*arg) { |
|
if (!strcasecmp(arg, "--short")) { |
|
style = style_short; |
|
} else if (!strcasecmp(arg, "--quiet")) { |
|
style = style_quiet; |
|
} else if (!strcasecmp(arg, "--detail")) { |
|
style = style_detail; |
|
} else if (!strcasecmp(arg, "--compact")) { |
|
style = style_compact; |
|
} else { |
|
scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); |
|
return; |
|
} |
|
} |
|
|
|
// Enter chat mode |
|
scr_set_chatmode(TRUE); |
|
scr_show_buddy_window(); |
|
|
|
bjid = buddy_getjid(bud); |
|
|
|
buffer = g_new(char, 4096); |
|
strncpy(buffer, "Room members:", 127); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
|
|
resources = buddy_getresources(bud); |
|
for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) { |
|
enum imstatus rstatus; |
|
const char *rst_msg; |
|
cnt++; |
|
|
|
rstatus = buddy_getstatus(bud, p_res->data); |
|
rst_msg = buddy_getstatusmsg(bud, p_res->data); |
|
|
|
if (style == style_short) { |
|
snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus], |
|
(char*)p_res->data, |
|
rst_msg ? " -- " : "", rst_msg ? rst_msg : ""); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
} else if (style == style_compact) { |
|
enum imrole role = buddy_getrole(bud, p_res->data); |
|
enum imaffiliation affil = buddy_getaffil(bud, p_res->data); |
|
bool showaffil = (affil != affil_none); |
|
|
|
snprintf(buffer, 4095, "[%c] %s (%s%s%s)", |
|
imstatus2char[rstatus], (char*)p_res->data, |
|
showaffil ? straffil[affil] : "\0", |
|
showaffil ? "/" : "\0", |
|
strrole[role]); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
} else { |
|
// (Style "normal", "detail" or "quiet") |
|
snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus], |
|
(char*)p_res->data); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
if (rst_msg && style != style_quiet) { |
|
snprintf(buffer, 4095, "Status message: %s", rst_msg); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
if (style == style_detail) { |
|
enum imrole role = buddy_getrole(bud, p_res->data); |
|
enum imaffiliation affil = buddy_getaffil(bud, p_res->data); |
|
|
|
snprintf(buffer, 4095, "Role: %s", strrole[role]); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
if (affil != affil_none) { |
|
snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]); |
|
scr_WriteIncomingMessage(bjid, buffer, |
|
0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); |
|
} |
|
} |
|
} |
|
g_free(p_res->data); |
|
} |
|
|
|
snprintf(buffer, 4095, "Total: %d member%c", cnt, cnt > 1 ? 's' : '\0'); |
|
scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0); |
|
|
|
g_slist_free(resources); |
|
g_free(buffer); |
|
} |
|
|
|
static void move_group_member(gpointer bud, void *groupnamedata) |
|
{ |
|
const char *bjid, *name, *groupname; |
|
guint type, on_srv; |
|
|
|
groupname = (char *)groupnamedata; |
|
|
|
bjid = buddy_getjid(bud); |
|
name = buddy_getname(bud); |
|
type = buddy_gettype(bud); |
|
on_srv = buddy_getonserverflag(bud); |
|
|
|
if (on_srv) { |
|
xmpp_updatebuddy(bjid, name, *groupname ? groupname : NULL); |
|
} else { |
|
buddy_setgroup(bud, (char *)groupname); |
|
if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) && |
|
settings_opt_get_int("muc_bookmark_autoupdate")) |
|
room_bookmark(bud, NULL); |
|
} |
|
} |
|
|
|
static void do_rename(char *arg) |
|
{ |
|
gpointer bud; |
|
const char *bjid, *group; |
|
guint type, on_srv; |
|
char *newname, *p; |
|
char *name_utf8; |
|
|
|
if (!current_buddy) |
|
return; |
|
bud = BUDDATA(current_buddy); |
|
|
|
bjid = buddy_getjid(bud); |
|
group = buddy_getgroupname(bud); |
|
type = buddy_gettype(bud); |
|
on_srv = buddy_getonserverflag(bud); |
|
|
|
if (type & ROSTER_TYPE_SPECIAL) { |
|
scr_LogPrint(LPRINT_NORMAL, "You can't rename this item."); |
|
return; |
|
} |
|
|
|
if (!*arg && !(type & ROSTER_TYPE_GROUP)) { |
|
scr_LogPrint(LPRINT_NORMAL, "Please specify a new name."); |
|
return; |
|
} |
|
|
|
//if (!(type & ROSTER_TYPE_GROUP) && !on_srv) { |
|
// scr_LogPrint(LPRINT_NORMAL, |
|
// "Note: this item will be added to your server roster."); |
|
// // If this is a MUC room w/o bookmark, let's give a small hint... |
|
// if ((type & ROSTER_TYPE_ROOM) && !xmpp_is_bookmarked(bjid)) { |
|
// scr_LogPrint(LPRINT_NORMAL, |
|
// "You should add a room bookmark or it will not be " |
|
// "recognized as a MUC room next time you run mcabber."); |
|
// } |
|
//} |
|
|
|
newname = g_strdup(arg); |
|
// Remove trailing space |
|
for (p = newname; *p; p++) ; |
|
while (p > newname && *p == ' ') *p = 0; |
|
|
|
strip_arg_special_chars(newname); |
|
|
|
name_utf8 = to_utf8(newname); |
|
|
|
if (type & ROSTER_TYPE_GROUP) { |
|
// Rename a whole group |
|
foreach_group_member(bud, &move_group_member, name_utf8); |
|
// Let's jump to the previous buddy, because this group name should |
|
// disappear when we receive the server answer. |
|
scr_roster_up_down(-1, 1); |
|
} else { |
|
// Rename a single buddy |
|
guint del_name = 0; |
|
if (!*newname || !strcmp(arg, "-")) |
|
del_name = TRUE; |
|
if (on_srv) { |
|
/* We do not rename the buddy right now because the server could reject |
|
* the request. Let's wait for the server answer. |
|
*/ |
|
xmpp_updatebuddy(bjid, (del_name ? NULL : name_utf8), |
|
group && *group ? group : NULL); |
|
} else { |
|
// This is a local item, we rename it without adding to roster. |
|
buddy_setname(bud, (del_name ? (char*)bjid : name_utf8)); |
|
if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) && |
|
settings_opt_get_int("muc_bookmark_autoupdate")) |
|
room_bookmark(bud, NULL); |
|
} |
|
} |
|
|
|
g_free(name_utf8); |
|
g_free(newname); |
|
scr_update_roster(); |
|
} |
|
|
|
static void do_move(char *arg) |
|
{ |
|
gpointer bud; |
|
const char *bjid, *name, *oldgroupname; |
|
guint type, on_srv; |
|
char *newgroupname, *p; |
|
char *group_utf8; |
|
|
|
if (!current_buddy) |
|
return; |
|
bud = BUDDATA(current_buddy); |
|
|
|
bjid = buddy_getjid(bud); |
|
name = buddy_getname(bud); |
|
type = buddy_gettype(bud); |
|
on_srv = buddy_getonserverflag(bud); |
|
|
|
oldgroupname = buddy_getgroupname(bud); |
|
|
|
if (type & ROSTER_TYPE_GROUP) { |
|
scr_LogPrint(LPRINT_NORMAL, "You can't move groups!"); |
|
return; |
|
} |
|
if (type & ROSTER_TYPE_SPECIAL) { |
|
scr_LogPrint(LPRINT_NORMAL, "You can't move this item."); |
|
return; |
|
} |
|
|
|
newgroupname = g_strdup(arg); |
|
// Remove trailing space |
|
for (p = newgroupname; *p; p++) ; |
|
while (p > newgroupname && *p == ' ') *p-- = 0; |
|
|
|
strip_arg_special_chars(newgroupname); |
|
|
|
group_utf8 = to_utf8(newgroupname); |
|
if (strcmp(oldgroupname, group_utf8)) { |
|
if (on_srv) { |
|
xmpp_updatebuddy(bjid, name, *group_utf8 ? group_utf8 : NULL); |
|
scr_roster_up_down(-1, 1); |
|
|
|
/* We do not move the buddy right now because the server could reject |
|
* the request. Let's wait for the server answer. |
|
*/ |
|
} else { |
|
// This is a local item, we move it without adding to roster. |
|
guint msgflag; |
|
|
|
// If the buddy has a pending message flag, |
|
// we remove it temporarily in order to reset the global group |
|
// flag. We set it back once the room is in the new group, |
|
// which will update the new group's flag. |
|
msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG; |
|
if (msgflag) |
|
roster_msg_setflag(bjid, FALSE, FALSE); |
|
buddy_setgroup(bud, group_utf8); |
|
if (msgflag) |
|
roster_msg_setflag(bjid, FALSE, TRUE); |
|
if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) && |
|
settings_opt_get_int("muc_bookmark_autoupdate")) |
|
room_bookmark(bud, NULL); |
|
} |
|
} |
|
|
|
g_free(group_utf8); |
|
g_free(newgroupname); |
|
scr_update_roster(); |
|
} |
|
|
|
static void list_option_cb(char *k, char *v, void *f) |
|
{ |
|
if (strncmp(k, "password", 8) && strcmp(k, "pgp_passphrase")) { |
|
GSList **list = f; |
|
*list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp); |
|
} |
|
} |
|
|
|
static void do_set(char *arg) |
|
{ |
|
guint assign; |
|
gchar *option, *value; |
|
gchar *option_utf8; |
|
|
|
if (!*arg) { |
|
// List all set options |
|
gsize max = 0; |
|
gsize maxmax = scr_gettextwidth() / 3; |
|
GSList *lel; |
|
gchar *format; |
|
GSList *list = NULL; |
|
// Get sorted list of keys |
|
settings_foreach(SETTINGS_TYPE_OPTION, list_option_cb, &list); |
|
if (!list) { |
|
scr_LogPrint(LPRINT_NORMAL, "No options found."); |
|
return; |
|
} |
|
// Find out maximum key length |
|
for (lel = list; lel; lel = lel->next) { |
|
const gchar *key = lel->data; |
|
gsize len = strlen(key); |
|
if (len > max) { |
|
max = len; |
|
if (max > maxmax) { |
|
max = maxmax; |
|
break; |
|
} |
|
} |
|
} |
|
// Print out list of options |
|
format = g_strdup_printf("%%-%us = [%%s]", (unsigned)max); |
|
for (lel = list; lel; lel = lel->next) { |
|
const gchar *key = lel->data; |
|
scr_LogPrint(LPRINT_NORMAL, format, key, settings_opt_get(key)); |
|
} |
|
g_free(format); |
|
scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE); |
|
scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE, |
|
ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max); |
|
return; |
|
} |
|
|
|
assign = parse_assigment(arg, &option, &value); |
|
if (!option) { |
|
scr_LogPrint(LPRINT_NORMAL, "Set what option?"); |
|
return; |
|
} |
|
option_utf8 = to_utf8(option); |
|
g_free(option); |
|
if (!assign) { // This is a query |
|
const gchar *val = settings_opt_get(option_utf8); |
|
if (val) { |
|
if (g_ascii_strncasecmp(option_utf8, "password", 8) == 0 || |
|
g_ascii_strcasecmp(option_utf8, "pgp_passphrase") == 0) |
|
val = "*** |