diff options
41 files changed, 1064 insertions, 446 deletions
diff --git a/CHANGELOG b/CHANGELOG index 645b1d79..4148ce86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,3 +9,7 @@ - /account remove - Added default account for /connect - Additional readline style shortcuts +- Improved chat session handling +- Override resource during chat and resource display settings (/resource) +- Disable terminal title by default, additonal title on exit +- Dynamic input blocking timeout to use less CPU diff --git a/Makefile.am b/Makefile.am index 520e4c19..9d44c003 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,6 +3,7 @@ core_sources = \ src/log.h src/profanity.c src/common.h \ src/profanity.h src/chat_session.c \ src/chat_session.h src/muc.c src/muc.h src/jid.h src/jid.c \ + src/chat_state.h src/chat_state.c \ src/resource.c src/resource.h \ src/roster_list.c src/roster_list.h \ src/xmpp/xmpp.h src/xmpp/capabilities.c src/xmpp/connection.c \ @@ -39,6 +40,7 @@ tests_sources = \ src/profanity.h src/chat_session.c \ src/chat_session.h src/muc.c src/muc.h src/jid.h src/jid.c \ src/resource.c src/resource.h \ + src/chat_state.h src/chat_state.c \ src/roster_list.c src/roster_list.h \ src/xmpp/xmpp.h src/xmpp/form.c \ src/ui/ui.h \ @@ -77,6 +79,7 @@ tests_sources = \ tests/test_cmd_statuses.c tests/test_cmd_statuses.h \ tests/test_cmd_sub.c tests/test_cmd_sub.h \ tests/test_cmd_win.c tests/test_cmd_win.h \ + tests/test_cmd_disconnect.c tests/test_cmd_disconnect.h \ tests/test_common.c tests/test_common.h \ tests/test_contact.c tests/test_contact.h \ tests/test_form.c tests/test_form.h \ @@ -88,6 +91,7 @@ tests_sources = \ tests/test_roster_list.c tests/test_roster_list.h \ tests/test_server_events.c tests/test_server_events.h \ tests/test_autocomplete.c tests/test_autocomplete.h \ + tests/test_chat_session.c tests/test_chat_session.h \ tests/testsuite.c main_source = src/main.c diff --git a/docs/profanity.1 b/docs/profanity.1 index 9cdf2d31..a65281fb 100644 --- a/docs/profanity.1 +++ b/docs/profanity.1 @@ -6,7 +6,7 @@ Profanity \- a simple console based XMPP chat client. [\-vhd] [\-l level] .SH DESCRIPTION .B Profanity -is a simple lightweight console based XMPP chat client. It's emphasis is +is a simple lightweight console based XMPP chat client. Its emphasis is on having a simple and configurable command driven UI, see the homepage at: .br diff --git a/src/chat_session.c b/src/chat_session.c index d27a3449..fbe06f76 100644 --- a/src/chat_session.c +++ b/src/chat_session.c @@ -34,6 +34,7 @@ #include <stdlib.h> #include <string.h> +#include <assert.h> #include <glib.h> @@ -42,41 +43,22 @@ #include "log.h" #include "xmpp/xmpp.h" -#define PAUSED_TIMOUT 10.0 -#define INACTIVE_TIMOUT 30.0 - -typedef enum { - CHAT_STATE_STARTED, - CHAT_STATE_ACTIVE, - CHAT_STATE_PAUSED, - CHAT_STATE_COMPOSING, - CHAT_STATE_INACTIVE, - CHAT_STATE_GONE -} chat_state_t; - -typedef struct chat_session_t { - char *barejid; - char *resource; - gboolean supported; - chat_state_t state; - GTimer *active_timer; - gboolean sent; -} ChatSession; - static GHashTable *sessions; -static ChatSession* -_chat_session_new(const char * const barejid, gboolean supported) +static void +_chat_session_new(const char * const barejid, const char * const resource, + gboolean resource_override, gboolean send_states) { + assert(barejid != NULL); + assert(resource != NULL); + ChatSession *new_session = malloc(sizeof(struct chat_session_t)); new_session->barejid = strdup(barejid); - new_session->resource = NULL; - new_session->supported = supported; - new_session->state = CHAT_STATE_STARTED; - new_session->active_timer = g_timer_new(); - new_session->sent = FALSE; + new_session->resource = strdup(resource); + new_session->resource_override = resource_override; + new_session->send_states = send_states; - return new_session; + g_hash_table_replace(sessions, strdup(barejid), new_session); } static void @@ -84,10 +66,7 @@ _chat_session_free(ChatSession *session) { if (session != NULL) { free(session->barejid); - if (session->active_timer != NULL) { - g_timer_destroy(session->active_timer); - session->active_timer = NULL; - } + free(session->resource); free(session); } } @@ -106,138 +85,75 @@ chat_sessions_clear(void) g_hash_table_remove_all(sessions); } -gboolean -chat_session_on_message_send(const char * const barejid) +void +chat_session_resource_override(const char * const barejid, const char * const resource) { - gboolean send_state = FALSE; - if (prefs_get_boolean(PREF_STATES)) { - ChatSession *session = g_hash_table_lookup(sessions, barejid); - if (!session) { - session = _chat_session_new(barejid, TRUE); - g_hash_table_insert(sessions, strdup(barejid), session); - - } - if (session->supported) { - session->state = CHAT_STATE_ACTIVE; - g_timer_start(session->active_timer); - session->sent = TRUE; - send_state = TRUE; - } - } + _chat_session_new(barejid, resource, TRUE, TRUE); +} - return send_state; +ChatSession* +chat_session_get(const char * const barejid) +{ + return g_hash_table_lookup(sessions, barejid); } void -chat_session_on_incoming_message(const char * const barejid, gboolean supported) +chat_session_recipient_gone(const char * const barejid, const char * const resource) { + assert(barejid != NULL); + assert(resource != NULL); + ChatSession *session = g_hash_table_lookup(sessions, barejid); - if (!session) { - session = _chat_session_new(barejid, supported); - g_hash_table_insert(sessions, strdup(barejid), session); - } else { - session->supported = supported; + if (session && g_strcmp0(session->resource, resource) == 0) { + if (!session->resource_override) { + chat_session_remove(barejid); + } } } void -chat_session_on_window_open(const char * const barejid) +chat_session_recipient_typing(const char * const barejid, const char * const resource) { - if (prefs_get_boolean(PREF_STATES)) { - ChatSession *session = g_hash_table_lookup(sessions, barejid); - if (!session) { - session = _chat_session_new(barejid, TRUE); - g_hash_table_insert(sessions, strdup(barejid), session); - } - } + chat_session_recipient_active(barejid, resource, TRUE); } void -chat_session_on_window_close(const char * const barejid) +chat_session_recipient_paused(const char * const barejid, const char * const resource) { - if (prefs_get_boolean(PREF_STATES)) { - ChatSession *session = g_hash_table_lookup(sessions, barejid); - // send <gone/> chat state before closing - if (session->supported) { - session->state = CHAT_STATE_GONE; - message_send_gone(barejid); - session->sent = TRUE; - g_hash_table_remove(sessions, barejid); - } - } + chat_session_recipient_active(barejid, resource, TRUE); } void -chat_session_on_cancel(const char * const jid) +chat_session_recipient_inactive(const char * const barejid, const char * const resource) { - if (prefs_get_boolean(PREF_STATES)) { - ChatSession *session = g_hash_table_lookup(sessions, jid); - if (session) { - session->supported = FALSE; - } - } + chat_session_recipient_active(barejid, resource, TRUE); } void -chat_session_on_activity(const char * const barejid) +chat_session_recipient_active(const char * const barejid, const char * const resource, + gboolean send_states) { + assert(barejid != NULL); + assert(resource != NULL); + ChatSession *session = g_hash_table_lookup(sessions, barejid); if (session) { - if (session->supported) { - if (session->state != CHAT_STATE_COMPOSING) { - session->sent = FALSE; - } - - session->state = CHAT_STATE_COMPOSING; - g_timer_start(session->active_timer); - - if (!session->sent || session->state == CHAT_STATE_PAUSED) { - message_send_composing(barejid); - session->sent = TRUE; - } + // session exists with resource, update chat_states + if (g_strcmp0(session->resource, resource) == 0) { + session->send_states = send_states; + // session exists with differet resource and no override, replace + } else if (!session->resource_override) { + _chat_session_new(barejid, resource, FALSE, send_states); } + + // no session, create one + } else { + _chat_session_new(barejid, resource, FALSE, send_states); } } void -chat_session_on_inactivity(const char * const barejid) +chat_session_remove(const char * const barejid) { - ChatSession *session = g_hash_table_lookup(sessions, barejid); - if (session && session->supported) { - if (session->active_timer != NULL) { - gdouble elapsed = g_timer_elapsed(session->active_timer, NULL); - - if ((prefs_get_gone() != 0) && (elapsed > (prefs_get_gone() * 60.0))) { - if (session->state != CHAT_STATE_GONE) { - session->sent = FALSE; - } - session->state = CHAT_STATE_GONE; - - } else if (elapsed > INACTIVE_TIMOUT) { - if (session->state != CHAT_STATE_INACTIVE) { - session->sent = FALSE; - } - session->state = CHAT_STATE_INACTIVE; - - } else if (elapsed > PAUSED_TIMOUT) { - if (session->state == CHAT_STATE_COMPOSING) { - session->sent = FALSE; - session->state = CHAT_STATE_PAUSED; - } - } - } - - if (session->sent == FALSE) { - if (session->state == CHAT_STATE_GONE) { - message_send_gone(barejid); - session->sent = TRUE; - } else if (session->state == CHAT_STATE_INACTIVE) { - message_send_inactive(barejid); - session->sent = TRUE; - } else if (session->state == CHAT_STATE_PAUSED && prefs_get_boolean(PREF_OUTTYPE)) { - message_send_paused(barejid); - session->sent = TRUE; - } - } - } + g_hash_table_remove(sessions, barejid); } \ No newline at end of file diff --git a/src/chat_session.h b/src/chat_session.h index d1815f44..585a523b 100644 --- a/src/chat_session.h +++ b/src/chat_session.h @@ -37,15 +37,27 @@ #include <glib.h> +typedef struct chat_session_t { + char *barejid; + char *resource; + gboolean resource_override; + gboolean send_states; + +} ChatSession; + void chat_sessions_init(void); void chat_sessions_clear(void); -gboolean chat_session_on_message_send(const char * const barejid); -void chat_session_on_window_open(const char * const barejid); -void chat_session_on_window_close(const char * const barejid); -void chat_session_on_incoming_message(const char * const barejid, gboolean supported); -void chat_session_on_cancel(const char * const jid); -void chat_session_on_activity(const char * const barejid); -void chat_session_on_inactivity(const char * const recipient); +void chat_session_resource_override(const char * const barejid, const char * const resource); +ChatSession* chat_session_get(const char * const barejid); + +void chat_session_recipient_active(const char * const barejid, const char * const resource, + gboolean send_states); +void chat_session_recipient_typing(const char * const barejid, const char * const resource); +void chat_session_recipient_paused(const char * const barejid, const char * const resource); +void chat_session_recipient_gone(const char * const barejid, const char * const resource); +void chat_session_recipient_inactive(const char * const barejid, const char * const resource); + +void chat_session_remove(const char * const barejid); #endif diff --git a/src/chat_state.c b/src/chat_state.c new file mode 100644 index 00000000..99a83f43 --- /dev/null +++ b/src/chat_state.c @@ -0,0 +1,172 @@ +/* + * chat_state.c + * + * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com> + * + * This file is part of Profanity. + * + * Profanity 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 3 of the License, or + * (at your option) any later version. + * + * Profanity 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 Profanity. If not, see <http://www.gnu.org/licenses/>. + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include <stdlib.h> + +#include <glib.h> + +#include "chat_state.h" +#include "chat_session.h" +#include "xmpp/xmpp.h" +#include "config/preferences.h" + +#define PAUSED_TIMEOUT 10.0 +#define INACTIVE_TIMEOUT 30.0 + +static void _send_if_supported(const char * const barejid, void(*send_func)(const char * const)); + +ChatState* +chat_state_new(void) +{ + ChatState *new_state = malloc(sizeof(struct prof_chat_state_t)); + new_state->type = CHAT_STATE_GONE; + new_state->timer = g_timer_new(); + + return new_state; +} + +void +chat_state_free(ChatState *state) +{ + if (state && state->timer!= NULL) { + g_timer_destroy(state->timer); + } + free(state); +} + +void +chat_state_handle_idle(const char * const barejid, ChatState *state) +{ + gdouble elapsed = g_timer_elapsed(state->timer, NULL); + + // TYPING -> PAUSED + if (state->type == CHAT_STATE_COMPOSING && elapsed > PAUSED_TIMEOUT) { + state->type = CHAT_STATE_PAUSED; + g_timer_start(state->timer); + if (prefs_get_boolean(PREF_STATES) && prefs_get_boolean(PREF_OUTTYPE)) { + _send_if_supported(barejid, message_send_paused); + } + return; + } + + // PAUSED|ACTIVE -> INACTIVE + if ((state->type == CHAT_STATE_PAUSED || state->type == CHAT_STATE_ACTIVE) && elapsed > INACTIVE_TIMEOUT) { + state->type = CHAT_STATE_INACTIVE; + g_timer_start(state->timer); + if (prefs_get_boolean(PREF_STATES)) { + _send_if_supported(barejid, message_send_inactive); + } + return; + + } + + // INACTIVE -> GONE + if (state->type == CHAT_STATE_INACTIVE) { + if (prefs_get_gone() != 0 && (elapsed > (prefs_get_gone() * 60.0))) { + ChatSession *session = chat_session_get(barejid); + if (session) { + // never move to GONE when resource override + if (!session->resource_override) { + if (prefs_get_boolean(PREF_STATES)) { + _send_if_supported(barejid, message_send_gone); + } + chat_session_remove(barejid); + state->type = CHAT_STATE_GONE; + g_timer_start(state->timer); + } + } else { + if (prefs_get_boolean(PREF_STATES)) { + message_send_gone(barejid); + } + state->type = CHAT_STATE_GONE; + g_timer_start(state->timer); + } + return; + } + } +} + +void +chat_state_handle_typing(const char * const barejid, ChatState *state) +{ + // ACTIVE|INACTIVE|PAUSED|GONE -> COMPOSING + if (state->type != CHAT_STATE_COMPOSING) { + state->type = CHAT_STATE_COMPOSING; + g_timer_start(state->timer); + if (prefs_get_boolean(PREF_STATES) && prefs_get_boolean(PREF_OUTTYPE)) { + _send_if_supported(barejid, message_send_composing); + } + } +} + +void +chat_state_active(ChatState *state) +{ + state->type = CHAT_STATE_ACTIVE; + g_timer_start(state->timer); +} + +void +chat_state_gone(const char * const barejid, ChatState *state) +{ + if (state->type != CHAT_STATE_GONE) { + if (prefs_get_boolean(PREF_STATES)) { + _send_if_supported(barejid, message_send_gone); + } + state->type = CHAT_STATE_GONE; + g_timer_start(state->timer); + } +} + +static void +_send_if_supported(const char * const barejid, void(*send_func)(const char * const)) +{ + gboolean send = TRUE; + GString *jid = g_string_new(barejid); + ChatSession *session = chat_session_get(barejid); + if (session) { + if (session->send_states) { + g_string_append(jid, "/"); + g_string_append(jid, session->resource); + } else { + send = FALSE; + } + } + + if (send) { + send_func(jid->str); + } + + g_string_free(jid, TRUE); +} \ No newline at end of file diff --git a/src/chat_state.h b/src/chat_state.h new file mode 100644 index 00000000..ba394a56 --- /dev/null +++ b/src/chat_state.h @@ -0,0 +1,61 @@ +/* + * chat_state.h + * + * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com> + * + * This file is part of Profanity. + * + * Profanity 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 3 of the License, or + * (at your option) any later version. + * + * Profanity 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 Profanity. If not, see <http://www.gnu.org/licenses/>. + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#ifndef CHAT_STATE_H +#define CHAT_STATE_H + +#include <glib.h> + +typedef enum { + CHAT_STATE_ACTIVE, + CHAT_STATE_COMPOSING, + CHAT_STATE_PAUSED, + CHAT_STATE_INACTIVE, + CHAT_STATE_GONE +} chat_state_type_t; + +typedef struct prof_chat_state_t { + chat_state_type_t type; + GTimer *timer; +} ChatState; + +ChatState* chat_state_new(void); +void chat_state_free(ChatState *state); + +void chat_state_handle_idle(const char * const barejid, ChatState *state); +void chat_state_handle_typing(const char * const barejid, ChatState *state); +void chat_state_active(ChatState *state); +void chat_state_gone(const char * const barejid, ChatState *state); + +#endif diff --git a/src/command/command.c b/src/command/command.c index e7d8547a..86185f59 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -96,6 +96,8 @@ static char * _ban_autocomplete(char *input, int *size); static char * _affiliation_autocomplete(char *input, int *size); static char * _role_autocomplete(char *input, int *size); static char * _resource_autocomplete(char *input, int *size); +static char * _titlebar_autocomplete(char *input, int *size); +static char * _inpblock_autocomplete(char *input, int *size); GHashTable *commands = NULL; @@ -278,13 +280,15 @@ static struct cmd_t command_defs[] = NULL } } }, { "/resource", - cmd_resource, parse_args, 1, 2, NULL, - { "/resource set|off [resource]", "Set the contact's resource.", - { "/resource set|off [resource]", - "----------------------------", - "Set the resource to use when chatting to a contact.", - "set resource - Set the resource.", - "off - Let the server choose which resource to route messages to.", + cmd_resource, parse_args, 1, 2, &cons_resource_setting, + { "/resource set|off|title|message [resource]", "Set the contact's resource.", + { "/resource set|off|title|message [resource]", + "------------------------------------------", + "Set the resource to use when chatting to a contact and manage resource display settings.", + "set resource - Set the resource.", + "off - Let the server choose which resource to route messages to.", + "title on|off - Show or hide the current resource in the titlebar.", + "message on|off - Show or hide the resource from which a message was recieved.", NULL } } }, { "/join", @@ -620,14 +624,18 @@ static struct cmd_t command_defs[] = NULL } } }, { "/inpblock", - cmd_inpblock, parse_args, 1, 1, &cons_inpblock_setting, - { "/inpblock millis", "Input blocking delay.", - { "/inpblock millis", - "----------------", - "Time to wait in milliseconds before reading input from the terminal buffer, defaults to 20.", - "Valid values are 1-1000.", - "A higher value will result in less CPU usage, but a noticable delay for response to input.", - "A lower value will result in higher CPU usage, but faster response to input.", + cmd_inpblock, parse_args, 2, 2, &cons_inpblock_setting, + { "/inpblock timeout|dynamic [millis|on|off]", "Input blocking delay (dynamic or static).", + { "/inpblock timeout|dynamic [millis|on|off]", + "-----------------------------------------", + "How long to wait for input before checking for new messages or checking for state changes such as 'idle'.", + "timeout : Time to wait in milliseconds before reading input from the terminal buffer, defaults to 500.", + " : Valid values are 1-1000.", + "dynamic : Start with a 0 timeout and increase the value dynamically up to the specified 'timeout'.", + " : on|off", + "A higher timeout will result in less CPU usage, but a noticable delay for response to input.", + "A lower timeout will result in higher CPU usage, but faster response to input.", + "Using the dynamic setting, higher CPU usage will occur during activity, but over time the CPU usage will decrease whilst there is no activity.", NULL } } }, { "/notify", @@ -719,11 +727,13 @@ static struct cmd_t command_defs[] = NULL } } }, { "/titlebar", - cmd_titlebar, parse_args, 1, 1, &cons_titlebar_setting, - { "/titlebar on|off", "Show information in the window title bar.", - { "/titlebar on|off", - "----------------", - "Show information in the window title bar.", + cmd_titlebar, parse_args, 2, 2, &cons_titlebar_setting, + { "/titlebar show|goodbye on|off", "Manage the terminal window title.", + { "/titlebar show|goodbye on|off", + "---------------------", + "Show or hide a title and exit message in the terminal window title.", + "show - Show current logged in user, and unread messages in the title.", + "goodbye - Show a message in the title when exiting profanity.", NULL } } }, { "/mouse", @@ -1110,6 +1120,7 @@ static Autocomplete occupants_ac; static Autocomplete occupants_default_ac; static Autocomplete time_ac; static Autocomplete resource_ac; +static Autocomplete inpblock_ac; /* * Initialise command autocompleter and history @@ -1205,7 +1216,8 @@ cmd_init(void) autocomplete_add(sub_ac, "received"); titlebar_ac = autocomplete_new(); - autocomplete_add(titlebar_ac, "version"); + autocomplete_add(titlebar_ac, "show"); + autocomplete_add(titlebar_ac, "goodbye"); log_ac = autocomplete_new(); autocomplete_add(log_ac, "maxsize"); @@ -1458,6 +1470,12 @@ cmd_init(void) resource_ac = autocomplete_new(); autocomplete_add(resource_ac, "set"); autocomplete_add(resource_ac, "off"); + autocomplete_add(resource_ac, "title"); + autocomplete_add(resource_ac, "message"); + + inpblock_ac = autocomplete_new(); + autocomplete_add(inpblock_ac, "timeout"); + autocomplete_add(inpblock_ac, "dynamic"); cmd_history_init(); } @@ -1515,6 +1533,7 @@ cmd_uninit(void) autocomplete_free(occupants_default_ac); autocomplete_free(time_ac); autocomplete_free(resource_ac); + autocomplete_free(inpblock_ac); } gboolean @@ -1660,6 +1679,7 @@ cmd_reset_autocomplete() autocomplete_reset(roster_option_ac); autocomplete_reset(roster_by_ac); autocomplete_reset(group_ac); + autocomplete_reset(titlebar_ac); autocomplete_reset(bookmark_ac); autocomplete_reset(bookmark_property_ac); autocomplete_reset(otr_ac); @@ -1682,6 +1702,7 @@ cmd_reset_autocomplete() autocomplete_reset(occupants_default_ac); autocomplete_reset(time_ac); autocomplete_reset(resource_ac); + autocomplete_reset(inpblock_ac); if (ui_current_win_type() == WIN_CHAT) { ProfChatWin *chatwin = wins_get_current_chat(); @@ -1817,8 +1838,7 @@ cmd_execute_default(const char * inp) if (otr_is_secure(chatwin->barejid)) { char *encrypted = otr_encrypt_message(chatwin->barejid, inp); if (encrypted != NULL) { - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, encrypted, send_state); + message_send_chat(chatwin->barejid, encrypted); otr_free_message(encrypted); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -1838,8 +1858,7 @@ cmd_execute_default(const char * inp) cons_show_error("Failed to send message."); } } else { - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, inp, send_state); + message_send_chat(chatwin->barejid, inp); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -1850,8 +1869,7 @@ cmd_execute_default(const char * inp) ui_outgoing_chat_msg("me", chatwin->barejid, inp); } #else - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, inp, send_state); + message_send_chat(chatwin->barejid, inp); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -1894,7 +1912,7 @@ _cmd_complete_parameters(char *input, int *size) // autocomplete boolean settings gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype", - "/flash", "/splash", "/chlog", "/grlog", "/mouse", "/history", "/titlebar", + "/flash", "/splash", "/chlog", "/grlog", "/mouse", "/history", "/vercheck", "/privileges", "/presence", "/wrap" }; for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) { @@ -2004,6 +2022,8 @@ _cmd_complete_parameters(char *input, int *size) g_hash_table_insert(ac_funcs, "/affiliation", _affiliation_autocomplete); g_hash_table_insert(ac_funcs, "/role", _role_autocomplete); g_hash_table_insert(ac_funcs, "/resource", _resource_autocomplete); + g_hash_table_insert(ac_funcs, "/titlebar", _titlebar_autocomplete); + g_hash_table_insert(ac_funcs, "/inpblock", _inpblock_autocomplete); char parsed[*size+1]; i = 0; @@ -2468,6 +2488,16 @@ _resource_autocomplete(char *input, int *size) } } + found = autocomplete_param_with_func(input, size, "/resource title", prefs_autocomplete_boolean_choice); + if (found != NULL) { + return found; + } + + found = autocomplete_param_with_func(input, size, "/resource message", prefs_autocomplete_boolean_choice); + if (found != NULL) { + return found; + } + found = autocomplete_param_with_ac(input, size, "/resource", resource_ac, FALSE); if (found != NULL) { return found; @@ -2477,6 +2507,47 @@ _resource_autocomplete(char *input, int *size) } static char * +_titlebar_autocomplete(char *input, int *size) +{ + char *found = NULL; + + found = autocomplete_param_with_func(input, size, "/titlebar show", prefs_autocomplete_boolean_choice); + if (found != NULL) { + return found; + } + + found = autocomplete_param_with_func(input, size, "/titlebar goodbye", prefs_autocomplete_boolean_choice); + if (found != NULL) { + return found; + } + + found = autocomplete_param_with_ac(input, size, "/titlebar", titlebar_ac, FALSE); + if (found != NULL) { + return found; + } + + return NULL; +} + +static char * +_inpblock_autocomplete(char *input, int *size) +{ + char *found = NULL; + + found = autocomplete_param_with_func(input, size, "/inpblock dynamic", prefs_autocomplete_boolean_choice); + if (found != NULL) { + return found; + } + + found = autocomplete_param_with_ac(input, size, "/inpblock", inpblock_ac, FALSE); + if (found != NULL) { + return found; + } + + return NULL; +} + +static char * _form_autocomplete(char *input, int *size) { ProfWin *current = wins_get_current(); diff --git a/src/command/commands.c b/src/command/commands.c index 1680c27b..3647bb22 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -1219,24 +1219,12 @@ cmd_msg(gchar **args, struct cmd_help_t help) barejid = usr; } - // if msg to current recipient, and resource specified, set resource - char *resource = NULL; - ProfWin *current = wins_get_current(); - if (current->type == WIN_CHAT) { - ProfChatWin *chatwin = (ProfChatWin*)current; - assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); - if ((g_strcmp0(chatwin->barejid, barejid) == 0) && (chatwin->resource)) { - resource = chatwin->resource; - } - } - if (msg != NULL) { #ifdef HAVE_LIBOTR if (otr_is_secure(barejid)) { char *encrypted = otr_encrypt_message(barejid, msg); if (encrypted != NULL) { - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, resource, encrypted, send_state); + message_send_chat(barejid, encrypted); otr_free_message(encrypted); ui_outgoing_chat_msg("me", barejid, msg); @@ -1265,13 +1253,11 @@ cmd_msg(gchar **args, struct cmd_help_t help) GString *otr_message = g_string_new(msg); g_string_append(otr_message, OTRL_MESSAGE_TAG_BASE); g_string_append(otr_message, OTRL_MESSAGE_TAG_V2); - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, resource, otr_message->str, send_state); + message_send_chat(barejid, otr_message->str); g_string_free(otr_message, TRUE); } else { - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, resource, msg, send_state); + message_send_chat(barejid, msg); } ui_outgoing_chat_msg("me", barejid, msg); @@ -1284,8 +1270,7 @@ cmd_msg(gchar **args, struct cmd_help_t help) } return TRUE; #else - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, resource, msg, send_state); + message_send_chat(barejid, msg); ui_outgoing_chat_msg("me", barejid, msg); if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { @@ -1298,7 +1283,6 @@ cmd_msg(gchar **args, struct cmd_help_t help) #endif } else { // msg == NULL - chat_session_on_window_open(barejid); ui_new_chat_win(barejid); #ifdef HAVE_LIBOTR if (otr_is_secure(barejid)) { @@ -1603,16 +1587,33 @@ cmd_roster(gchar **args, struct cmd_help_t help) gboolean cmd_resource(gchar **args, struct cmd_help_t help) { + char *cmd = args[0]; + char *setting = NULL; + if (g_strcmp0(cmd, "message") == 0) { + setting = args[1]; + if (!setting) { + cons_show("Usage: %s", help.usage); + return TRUE; + } else { + return _cmd_set_boolean_preference(setting, help, "Message resource", PREF_RESOURCE_MESSAGE); + } + } else if (g_strcmp0(cmd, "title") == 0) { + setting = args[1]; + if (!setting) { + cons_show("Usage: %s", help.usage); + return TRUE; + } else { + return _cmd_set_boolean_preference(setting, help, "Title resource", PREF_RESOURCE_TITLE); + } + } + ProfWin *current = wins_get_current(); if (current->type != WIN_CHAT) { - cons_show("The /resource command is only valid in chat windows."); + cons_show("Resource can only be changed in chat windows."); return TRUE; } - ProfChatWin *chatwin = (ProfChatWin*)current; - char *cmd = args[0]; - if (g_strcmp0(cmd, "set") == 0) { char *resource = args[1]; if (!resource) { @@ -1638,11 +1639,17 @@ cmd_resource(gchar **args, struct cmd_help_t help) return TRUE; } - chatwin->resource = strdup(resource); + chatwin->resource_override = strdup(resource); + chat_state_free(chatwin->state); + chatwin->state = chat_state_new(); + chat_session_resource_override(chatwin->barejid, resource); return TRUE; } else if (g_strcmp0(cmd, "off") == 0) { - FREE_SET_NULL(chatwin->resource); + FREE_SET_NULL(chatwin->resource_override); + chat_state_free(chatwin->state); + chatwin->state = chat_state_new(); + chat_session_remove(chatwin->barejid); return TRUE; } else { cons_show("Usage: %s", help.usage); @@ -3025,8 +3032,7 @@ cmd_tiny(gchar **args, struct cmd_help_t help) if (otr_is_secure(chatwin->barejid)) { char *encrypted = otr_encrypt_message(chatwin->barejid, tiny); if (encrypted != NULL) { - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, encrypted, send_state); + message_send_chat(chatwin->barejid, encrypted); otr_free_message(encrypted); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -3046,8 +3052,7 @@ cmd_tiny(gchar **args, struct cmd_help_t help) cons_show_error("Failed to send message."); } } else { - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, tiny, send_state); + message_send_chat(chatwin->barejid, tiny); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -3058,8 +3063,7 @@ cmd_tiny(gchar **args, struct cmd_help_t help) ui_outgoing_chat_msg("me", chatwin->barejid, tiny); } #else - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, chatwin->resource, tiny, send_state); + message_send_chat(chatwin->barejid, tiny); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -3264,10 +3268,18 @@ cmd_states(gchar **args, struct cmd_help_t help) gboolean cmd_titlebar(gchar **args, struct cmd_help_t help) { - if (g_strcmp0(args[0], "off") == 0) { + if (g_strcmp0(args[0], "show") != 0 && g_strcmp0(args[0], "goodbye") != 0) { + cons_show("Usage: %s", help.usage); + return TRUE; + } + if (g_strcmp0(args[0], "show") == 0 && g_strcmp0(args[1], "off") == 0) { ui_clear_win_title(); } - return _cmd_set_boolean_preference(args[0], help, "Titlebar", PREF_TITLEBAR); + if (g_strcmp0(args[0], "show") == 0) { + return _cmd_set_boolean_preference(args[1], help, "Titlebar show", PREF_TITLEBAR_SHOW); + } else { + return _cmd_set_boolean_preference(args[1], help, "Titlebar goodbye", PREF_TITLEBAR_GOODBYE); + } } gboolean @@ -3454,13 +3466,41 @@ cmd_notify(gchar **args, struct cmd_help_t help) gboolean cmd_inpblock(gchar **args, struct cmd_help_t help) { - char *value = args[0]; + char *subcmd = args[0]; + char *value = args[1]; int intval; - if (_strtoi(value, &intval, 1, 1000) == 0) { - cons_show("Input blocking set to %d milliseconds.", intval); - prefs_set_inpblock(intval); - ui_input_nonblocking(); + + if (g_strcmp0(subcmd, "timeout") == 0) { + if (value == NULL) { + cons_show("Usage: %s", help.usage); + return TRUE; + } + + if (_strtoi(value, &intval, 1, 1000) == 0) { + cons_show("Input blocking set to %d milliseconds.", intval); + prefs_set_inpblock(intval); + ui_input_nonblocking(FALSE); + } + + return TRUE; + } + + if (g_strcmp0(subcmd, "dynamic") == 0) { + if (value == NULL) { + cons_show("Usage: %s", help.usage); + return TRUE; + } + + if (g_strcmp0(value, "on") != 0 && g_strcmp0(value, "off") != 0) { + cons_show("Dynamic must be one of 'on' or 'off'"); + return TRUE; + } + + return _cmd_set_boolean_preference(value, help, "Dynamic input blocking", PREF_INPBLOCK_DYNAMIC); } + + cons_show("Usage: %s", help.usage); + return TRUE; } @@ -3967,7 +4007,6 @@ cmd_otr(gchar **args, struct cmd_help_t help) barejid = contact; } - chat_session_on_window_open(barejid); ui_new_chat_win(barejid); if (ui_current_win_is_otr()) { @@ -3977,8 +4016,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) ui_current_print_formatted_line('!', 0, "You have not generated or loaded a private key, use '/otr gen'"); } else if (!otr_is_secure(barejid)) { char *otr_query_message = otr_start_query(); - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, NULL, otr_query_message, send_state); + message_send_chat(barejid, otr_query_message); } else { ui_gone_secure(barejid, otr_is_trusted(barejid)); } @@ -3996,8 +4034,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) } else { ProfChatWin *chatwin = ui_get_current_chat(); char *otr_query_message = otr_start_query(); - gboolean send_state = chat_session_on_message_send(chatwin->barejid); - message_send_chat(chatwin->barejid, NULL, otr_query_message, send_state); + message_send_chat(chatwin->barejid, otr_query_message); } } } diff --git a/src/command/commands.h b/src/command/commands.h index dd09fae0..f4e040a9 100644 --- a/src/command/commands.h +++ b/src/command/commands.h @@ -140,4 +140,4 @@ gboolean cmd_inpblock(gchar **args, struct cmd_help_t help); gboolean cmd_form_field(char *tag, gchar **args); -#endif \ No newline at end of file +#endif diff --git a/src/config/preferences.c b/src/config/preferences.c index 22e80874..5b683426 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -52,6 +52,7 @@ #include "preferences.h" #include "tools/autocomplete.h" +// preference groups refer to the sections in .profrc, for example [ui] #define PREF_GROUP_LOGGING "logging" #define PREF_GROUP_CHATSTATES "chatstates" #define PREF_GROUP_UI "ui" @@ -127,6 +128,16 @@ prefs_load(void) g_error_free(err); } + // move pre 0.4.6 titlebar preference + err = NULL; + gchar *old_titlebar = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar", &err); + if (err == NULL) { + g_key_file_set_string(prefs, PREF_GROUP_UI, _get_key(PREF_TITLEBAR_SHOW), old_titlebar); + g_key_file_remove_key(prefs, PREF_GROUP_UI, "titlebar", NULL); + } else { + g_error_free(err); + } + _save_prefs(); boolean_choice_ac = autocomplete_new(); @@ -487,6 +498,9 @@ _get_preferences_file(void) return result; } +// get the preference group for a specific preference +// for example the PREF_BEEP setting ("beep" in .profrc, see _get_key) belongs +// to the [ui] section. static const char * _get_group(preference_t pref) { @@ -496,7 +510,8 @@ _get_group(preference_t pref) case PREF_BEEP: case PREF_THEME: case PREF_VERCHECK: - case PREF_TITLEBAR: + case PREF_TITLEBAR_SHOW: + case PREF_TITLEBAR_GOODBYE: case PREF_FLASH: case PREF_INTYPE: case PREF_HISTORY: @@ -514,6 +529,9 @@ _get_group(preference_t pref) case PREF_ROSTER_OFFLINE: case PREF_ROSTER_RESOURCE: case PREF_ROSTER_BY: + case PREF_RESOURCE_TITLE: + case PREF_RESOURCE_MESSAGE: + case PREF_INPBLOCK_DYNAMIC: return PREF_GROUP_UI; case PREF_STATES: case PREF_OUTTYPE: @@ -550,6 +568,8 @@ _get_group(preference_t pref) } } +// get the key used in .profrc for the preference +// for example the PREF_AUTOAWAY_MODE maps to "autoaway.mode" in .profrc static const char * _get_key(preference_t pref) { @@ -563,8 +583,10 @@ _get_key(preference_t pref) return "theme"; case PREF_VERCHECK: return "vercheck"; - case PREF_TITLEBAR: - return "titlebar"; + case PREF_TITLEBAR_SHOW: + return "titlebar.show"; + case PREF_TITLEBAR_GOODBYE: + return "titlebar.goodbye"; case PREF_FLASH: return "flash"; case PREF_INTYPE: @@ -647,11 +669,19 @@ _get_key(preference_t pref) return "roster.resource"; case PREF_ROSTER_BY: return "roster.by"; + case PREF_RESOURCE_TITLE: + return "resource.title"; + case PREF_RESOURCE_MESSAGE: + return "resource.message"; + case PREF_INPBLOCK_DYNAMIC: + return "inpblock.dynamic"; default: return NULL; } } +// the default setting for a boolean type preference +// if it is not specified in .profrc static gboolean _get_default_boolean(preference_t pref) { @@ -669,12 +699,15 @@ _get_default_boolean(preference_t pref) case PREF_MUC_PRIVILEGES: case PREF_PRESENCE: case PREF_WRAP: + case PREF_INPBLOCK_DYNAMIC: return TRUE; default: return FALSE; } } +// the default setting for a string type preference +// if it is not specified in .profrc static char * _get_default_string(preference_t pref) { diff --git a/src/config/preferences.h b/src/config/preferences.h index c8b206ef..9590eb64 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -47,12 +47,15 @@ #define PREFS_MIN_LOG_SIZE 64 #define PREFS_MAX_LOG_SIZE 1048580 +// represents all settings in .profrc +// each enum value is mapped to a group and key in .profrc (see preferences.c) typedef enum { PREF_SPLASH, PREF_BEEP, PREF_VERCHECK, PREF_THEME, - PREF_TITLEBAR, + PREF_TITLEBAR_SHOW, + PREF_TITLEBAR_GOODBYE, PREF_FLASH, PREF_INTYPE, PREF_HISTORY, @@ -95,7 +98,10 @@ typedef enum { PREF_LOG_SHARED, PREF_OTR_LOG, PREF_OTR_WARN, - PREF_OTR_POLICY + PREF_OTR_POLICY, + PREF_RESOURCE_TITLE, + PREF_RESOURCE_MESSAGE, + PREF_INPBLOCK_DYNAMIC } preference_t; typedef struct prof_alias_t { diff --git a/src/otr/otr.c b/src/otr/otr.c index 680c4c10..bd1c2ce3 100644 --- a/src/otr/otr.c +++ b/src/otr/otr.c @@ -110,8 +110,7 @@ static void cb_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { - gboolean send_state = chat_session_on_message_send(recipient); - message_send_chat(recipient, NULL, message, send_state); + message_send_chat(recipient, message); } static void diff --git a/src/profanity.c b/src/profanity.c index e53ac176..b28eae20 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -46,6 +46,7 @@ #include "profanity.h" #include "chat_session.h" +#include "chat_state.h" #include "config/accounts.h" #include "config/preferences.h" #include "config/theme.h" @@ -75,7 +76,7 @@ prof_run(const int disable_tls, char *log_level, char *account_name) { _init(disable_tls, log_level); log_info("Starting main event loop"); - ui_input_nonblocking(); + ui_input_nonblocking(TRUE); GTimer *timer = g_timer_new(); gboolean cmd_result = TRUE; jabber_conn_status_t conn_status = jabber_get_connection_status(); @@ -142,7 +143,8 @@ prof_handle_idle(void) while (curr != NULL) { char *barejid = curr->data; - chat_session_on_inactivity(barejid); + ProfChatWin *chatwin = wins_get_chat(barejid); + chat_state_handle_idle(chatwin->barejid, chatwin->state); curr = g_slist_next(curr); } @@ -160,7 +162,7 @@ prof_handle_activity(void) if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) { ProfChatWin *chatwin = wins_get_current_chat(); - chat_session_on_activity(chatwin->barejid); + chat_state_handle_typing(chatwin->barejid, chatwin->state); } } @@ -290,7 +292,13 @@ _init(const int disable_tls, char *log_level) static void _shutdown(void) { - ui_clear_win_title(); + if (prefs_get_boolean(PREF_TITLEBAR_SHOW)) { + if (prefs_get_boolean(PREF_TITLEBAR_GOODBYE)) { + ui_goodbye_title(); + } else { + ui_clear_win_title(); + } + } ui_close_all_wins(); jabber_disconnect(); jabber_shutdown(); diff --git a/src/server_events.c b/src/server_events.c index 9d7883d6..fbf534ac 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -86,8 +86,9 @@ handle_message_error(const char * const jid, const char * const type, // handle recipient not found ('from' contains a value and type is 'cancel') } else if (type != NULL && (strcmp(type, "cancel") == 0)) { - ui_handle_recipient_not_found(jid, err_msg); - chat_session_on_cancel(jid); + log_info("Recipient %s not found: %s", jid, err_msg); + Jid *jidp = jid_create(jid); + chat_session_remove(jidp->barejid); // handle any other error from recipient } else { @@ -296,7 +297,7 @@ handle_incoming_private_message(char *fulljid, char *message) } void -handle_incoming_message(char *barejid, char *message) +handle_incoming_message(char *barejid, char *resource, char *message) { #ifdef HAVE_LIBOTR gboolean was_decrypted = FALSE; @@ -318,8 +319,7 @@ handle_incoming_message(char *barejid, char *message) memmove(whitespace_base, whitespace_base+tag_length, tag_length); char *otr_query_message = otr_start_query(); cons_show("OTR Whitespace pattern detected. Attempting to start OTR session..."); - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, NULL, otr_query_message, send_state); + message_send_chat(barejid, otr_query_message); } } } @@ -333,11 +333,10 @@ handle_incoming_message(char *barejid, char *message) if (policy == PROF_OTRPOLICY_ALWAYS && !was_decrypted && !whitespace_base) { char *otr_query_message = otr_start_query(); cons_show("Attempting to start OTR session..."); - gboolean send_state = chat_session_on_message_send(barejid); - message_send_chat(barejid, NULL, otr_query_message, send_state); + message_send_chat(barejid, otr_query_message); } - ui_incoming_msg(barejid, newmessage, NULL); + ui_incoming_msg(barejid, resource, newmessage, NULL); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -356,7 +355,7 @@ handle_incoming_message(char *barejid, char *message) otr_free_message(newmessage); #else - ui_incoming_msg(barejid, message, NULL); + ui_incoming_msg(barejid, resource, message, NULL); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -376,7 +375,7 @@ handle_delayed_private_message(char *fulljid, char *message, GTimeVal tv_stamp) void handle_delayed_message(char *barejid, char *message, GTimeVal tv_stamp) { - ui_incoming_msg(barejid, message, &tv_stamp); + ui_incoming_msg(barejid, NULL, message, &tv_stamp); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -387,15 +386,45 @@ handle_delayed_message(char *barejid, char *message, GTimeVal tv_stamp) } void -handle_typing(char *from) +handle_typing(char *barejid, char *resource) +{ + ui_contact_typing(barejid, resource); + if (ui_chat_win_exists(barejid)) { + chat_session_recipient_typing(barejid, resource); + } +} + +void +handle_paused(char *barejid, char *resource) +{ + if (ui_chat_win_exists(barejid)) { + chat_session_recipient_paused(barejid, resource); + } +} + +void +handle_inactive(char *barejid, char *resource) +{ + if (ui_chat_win_exists(barejid)) { + chat_session_recipient_inactive(barejid, resource); + } +} + +void +handle_gone(const char * const barejid, const char * const resource) { - ui_contact_typing(from); + ui_recipient_gone(barejid, resource); + if (ui_chat_win_exists(barejid)) { + chat_session_recipient_gone(barejid, resource); + } } void -handle_gone(const char * const from) +handle_activity(const char * const barejid, const char * const resource, gboolean send_states) { - ui_recipient_gone(from); + if (ui_chat_win_exists(barejid)) { + chat_session_recipient_active(barejid, resource, send_states); + } } void @@ -433,38 +462,11 @@ handle_contact_offline(char *barejid, char *resource, char *status) gboolean updated = roster_contact_offline(barejid, resource, status); if (resource != NULL && updated) { - char *show_console = prefs_get_string(PREF_STATUSES_CONSOLE); - char *show_chat_win = prefs_get_string(PREF_STATUSES_CHAT); - Jid *jid = jid_create_from_bare_and_resource(barejid, resource); - PContact contact = roster_get_contact(barejid); - if (p_contact_subscription(contact) != NULL) { - if (strcmp(p_contact_subscription(contact), "none") != 0) { - - // show in console if "all" - if (g_strcmp0(show_console, "all") == 0) { - cons_show_contact_offline(contact, resource, status); - - // show in console of "online" - } else if (g_strcmp0(show_console, "online") == 0) { - cons_show_contact_offline(contact, resource, status); - } - - // show in chat win if "all" - if (g_strcmp0(show_chat_win, "all") == 0) { - ui_chat_win_contact_offline(contact, resource, status); - - // show in char win if "online" and presence online - } else if (g_strcmp0(show_chat_win, "online") == 0) { - ui_chat_win_contact_offline(contact, resource, status); - } - } - } - prefs_free_string(show_console); - prefs_free_string(show_chat_win); - jid_destroy(jid); + ui_contact_offline(barejid, resource, status); } rosterwin_roster(); + chat_session_remove(barejid); } void @@ -507,6 +509,7 @@ handle_contact_online(char *barejid, Resource *resource, } rosterwin_roster(); + chat_session_remove(barejid); } void diff --git a/src/server_events.h b/src/server_events.h index 32ac62f3..6a12dc6e 100644 --- a/src/server_events.h +++ b/src/server_events.h @@ -69,12 +69,15 @@ void handle_room_role_list(const char * const from, const char * const role, GSL void handle_room_role_set_error(const char * const room, const char * const nick, const char * const role, const char * const error); void handle_room_kick_result_error(const char * const room, const char * const nick, const char * const error); -void handle_incoming_message(char *barejid, char *message); +void handle_incoming_message(char *barejid, char *resource, char *message); void handle_incoming_private_message(char *fulljid, char *message); void handle_delayed_message(char *fulljid, char *message, GTimeVal tv_stamp); void handle_delayed_private_message(char *fulljid, char *message, GTimeVal tv_stamp); -void handle_typing(char *from); -void handle_gone(const char * const from); +void handle_typing(char *barejid, char *resource); +void handle_paused(char *barejid, char *resource); +void handle_inactive(char *barejid, char *resource); +void handle_activity(char *barejid, char *resource, gboolean send_states); +void handle_gone(const char * const barejid, const char * const resource); void handle_subscription(const char *from, jabber_subscr_t type); void handle_contact_offline(char *contact, char *resource, char *status); void handle_contact_online(char *contact, Resource *resource, diff --git a/src/ui/console.c b/src/ui/console.c index 3d3a5d8b..8d1f3173 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -841,6 +841,19 @@ cons_beep_setting(void) } void +cons_resource_setting(void) +{ + if (prefs_get_boolean(PREF_RESOURCE_TITLE)) + cons_show("Resource title (/resource) : ON"); + else + cons_show("Resource title (/resource) : OFF"); + if (prefs_get_boolean(PREF_RESOURCE_MESSAGE)) + cons_show("Message title (/resource) : ON"); + else + cons_show("Message title (/resource) : OFF"); +} + +void cons_wrap_setting(void) { if (prefs_get_boolean(PREF_WRAP)) @@ -951,10 +964,15 @@ cons_statuses_setting(void) void cons_titlebar_setting(void) { - if (prefs_get_boolean(PREF_TITLEBAR)) { - cons_show("Titlebar display (/titlebar) : ON"); + if (prefs_get_boolean(PREF_TITLEBAR_SHOW)) { + cons_show("Titlebar show (/titlebar) : ON"); + } else { + cons_show("Titlebar show (/titlebar) : OFF"); + } + if (prefs_get_boolean(PREF_TITLEBAR_GOODBYE)) { + cons_show("Titlebar goodbye (/titlebar) : ON"); } else { - cons_show("Titlebar display (/titlebar) : OFF"); + cons_show("Titlebar goodbye (/titlebar) : OFF"); } } @@ -991,6 +1009,7 @@ cons_show_ui_prefs(void) cons_splash_setting(); cons_wrap_setting(); cons_time_setting(); + cons_resource_setting(); cons_vercheck_setting(); cons_mouse_setting(); cons_statuses_setting(); @@ -1163,7 +1182,12 @@ cons_show_chat_prefs(void) void cons_inpblock_setting(void) { - cons_show("Input block (/inpblock) : %d milliseconds", prefs_get_inpblock()); + cons_show("Input timeout (/inpblock) : %d milliseconds", prefs_get_inpblock()); + if (prefs_get_boolean(PREF_INPBLOCK_DYNAMIC)) { + cons_show("Dynamic timeout (/inpblock) : ON"); + } else { + cons_show("Dynamic timeout (/inpblock) : OFF"); + } } void diff --git a/src/ui/core.c b/src/ui/core.c index 2e8ae9ff..e0f46f6e 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -122,7 +122,7 @@ ui_update(void) win_update_virtual(current); - if (prefs_get_boolean(PREF_TITLEBAR)) { + if (prefs_get_boolean(PREF_TITLEBAR_SHOW)) { _ui_draw_term_title(); } title_bar_update_virtual(); @@ -180,7 +180,11 @@ ui_get_char(char *input, int *size, int *result) wint_t ch = inp_get_char(input, size, result); if (ch != ERR && *result != ERR) { ui_reset_idle_time(); + ui_input_nonblocking(TRUE); + } else { + ui_input_nonblocking(FALSE); } + return ch; } @@ -197,9 +201,34 @@ ui_replace_input(char *input, const char * const new_input, int *size) } void -ui_input_nonblocking(void) +ui_input_nonblocking(gboolean reset) { - inp_non_block(); + static gint timeout = 0; + static gint no_input_count = 0; + + if (! prefs_get_boolean(PREF_INPBLOCK_DYNAMIC)) { + inp_non_block(prefs_get_inpblock()); + return; + } + + if (reset) { + timeout = 0; + no_input_count = 0; + } + + if (timeout < prefs_get_inpblock()) { + no_input_count++; + + if (no_input_count % 10 == 0) { + timeout += no_input_count; + + if (timeout > prefs_get_inpblock()) { + timeout = prefs_get_inpblock(); + } + } + } + + inp_non_block(timeout); } void @@ -272,11 +301,19 @@ ui_handle_stanza(const char * const msg) } } +gboolean +ui_chat_win_exists(const char * const barejid) +{ + ProfChatWin *chatwin = wins_get_chat(barejid); + return (chatwin != NULL); +} + void -ui_contact_typing(const char * const barejid) +ui_contact_typing(const char * const barejid, const char * const resource) { ProfChatWin *chatwin = wins_get_chat(barejid); ProfWin *window = (ProfWin*) chatwin; + ChatSession *session = chat_session_get(barejid); if (prefs_get_boolean(PREF_INTYPE)) { // no chat window for user @@ -287,8 +324,8 @@ ui_contact_typing(const char * const barejid) } else if (!wins_is_current(window)) { cons_show_typing(barejid); - // in chat window with user - } else { + // in chat window with user, no session or session with resource + } else if (!session || (session && g_strcmp0(session->resource, resource) == 0)) { title_bar_set_typing(TRUE); int num = wins_get_num(window); @@ -328,20 +365,25 @@ ui_get_current_chat(void) } void -ui_incoming_msg(const char * const barejid, const char * const message, GTimeVal *tv_stamp) +ui_incoming_msg(const char * const barejid, const char * const resource, const char * const message, GTimeVal *tv_stamp) { gboolean win_created = FALSE; - char *display_from = NULL; + GString *user = g_string_new(""); PContact contact = roster_get_contact(barejid); if (contact != NULL) { if (p_contact_name(contact) != NULL) { - display_from = strdup(p_contact_name(contact)); + g_string_append(user, p_contact_name(contact)); } else { - display_from = strdup(barejid); + g_string_append(user, barejid); } } else { - display_from = strdup(barejid); + g_string_append(user, barejid); + } + + if (resource && prefs_get_boolean(PREF_RESOURCE_MESSAGE)) { + g_string_append(user, "/"); + g_string_append(user, resource); } ProfChatWin *chatwin = wins_get_chat(barejid); @@ -362,14 +404,14 @@ ui_incoming_msg(const char * const barejid, const char * const message, GTimeVal // currently viewing chat window with sender if (wins_is_current(window)) { - win_print_incoming_message(window, tv_stamp, display_from, message); + win_print_incoming_message(window, tv_stamp, user->str, message); title_bar_set_typing(FALSE); status_bar_active(num); // not currently viewing chat window with sender } else { status_bar_new(num); - cons_show_incoming_message(display_from, num); + cons_show_incoming_message(user->str, num); if (prefs_get_boolean(PREF_FLASH)) { flash(); @@ -388,7 +430,7 @@ ui_incoming_msg(const char * const barejid, const char * const message, GTimeVal } } - win_print_incoming_message(window, tv_stamp, display_from, message); + win_print_incoming_message(window, tv_stamp, user->str, message); } int ui_index = num; @@ -404,14 +446,14 @@ ui_incoming_msg(const char * const barejid, const char * const message, GTimeVal gboolean is_current = wins_is_current(window); if ( !is_current || (is_current && prefs_get_boolean(PREF_NOTIFY_MESSAGE_CURRENT)) ) { if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_TEXT)) { - notify_message(display_from, ui_index, message); + notify_message(user->str, ui_index, message); } else { - notify_message(display_from, ui_index, NULL); + notify_message(user->str, ui_index, NULL); } } } - free(display_from); + g_string_free(user, TRUE); } void @@ -586,14 +628,6 @@ ui_update_presence(const resource_presence_t resource_presence, void ui_handle_recipient_not_found(const char * const recipient, const char * const err_msg) { - // unknown chat recipient - ProfChatWin *chatwin = wins_get_chat(recipient); - if (chatwin) { - cons_show_error("Recipient %s not found: %s", recipient, err_msg); - win_save_vprint((ProfWin*) chatwin, '!', NULL, 0, THEME_ERROR, "", "Recipient %s not found: %s", recipient, err_msg); - return; - } - // intended recipient was invalid chat room ProfMucWin *mucwin = wins_get_muc(recipient); if (mucwin) { @@ -601,17 +635,6 @@ ui_handle_recipient_not_found(const char * const recipient, const char * const e win_save_vprint((ProfWin*) mucwin, '!', NULL, 0, THEME_ERROR, "", "Room %s not found: %s", recipient, err_msg); return; } - - // unknown private recipient - ProfPrivateWin *privatewin = wins_get_private(recipient); - if (privatewin) { - cons_show_error("Recipient %s not found: %s", recipient, err_msg); - win_save_vprint((ProfWin*) privatewin, '!', NULL, 0, THEME_ERROR, "", "Recipient %s not found: %s", recipient, err_msg); - return; - } - - // no window - cons_show_error("Recipient %s not found: %s", recipient, err_msg); } void @@ -705,7 +728,8 @@ ui_close_connected_win(int index) otr_end_session(chatwin->barejid); } #endif - chat_session_on_window_close(chatwin->barejid); + chat_state_gone(chatwin->barejid, chatwin->state); + chat_session_remove(chatwin->barejid); } } } @@ -945,8 +969,6 @@ ui_gone_secure(const char * const barejid, gboolean trusted) chatwin = (ProfChatWin*)window; } - FREE_SET_NULL(chatwin->resource); - chatwin->is_otr = TRUE; chatwin->is_trusted = trusted; if (trusted) { @@ -1162,7 +1184,7 @@ ui_prune_wins(void) if (window->type == WIN_CHAT) { if (conn_status == JABBER_CONNECTED) { ProfChatWin *chatwin = (ProfChatWin*)window; - chat_session_on_window_close(chatwin->barejid); + chat_session_remove(chatwin->barejid); } } @@ -1280,26 +1302,36 @@ ui_print_system_msg_from_recipient(const char * const barejid, const char *messa } void -ui_recipient_gone(const char * const barejid) +ui_recipient_gone(const char * const barejid, const char * const resource) { if (barejid == NULL) return; + if (resource == NULL) + return; - const char * display_usr = NULL; - PContact contact = roster_get_contact(barejid); - if (contact != NULL) { - if (p_contact_name(contact) != NULL) { - display_usr = p_contact_name(contact); - } else { - display_usr = barejid; + gboolean show_message = TRUE; + + ProfChatWin *chatwin = wins_get_chat(barejid); + if (chatwin) { + ChatSession *session = chat_session_get(barejid); + if (session && g_strcmp0(session->resource, resource) != 0) { + show_message = FALSE; } - } else { - display_usr = barejid; - } + if (show_message) { + const char * display_usr = NULL; + PContact contact = roster_get_contact(barejid); + if (contact != NULL) { + if (p_contact_name(contact) != NULL) { + display_usr = p_contact_name(contact); + } else { + display_usr = barejid; + } + } else { + display_usr = barejid; + } - ProfWin *window = (ProfWin*)wins_get_chat(barejid); - if (window != NULL) { - win_save_vprint(window, '!', NULL, 0, THEME_GONE, "", "<- %s has left the conversation.", display_usr); + win_save_vprint((ProfWin*)chatwin, '!', NULL, 0, THEME_GONE, "", "<- %s has left the conversation.", display_usr); + } } } @@ -1381,9 +1413,9 @@ ui_outgoing_chat_msg(const char * const from, const char * const barejid, // create new window if (window == NULL) { window = wins_new_chat(barejid); + ProfChatWin *chatwin = (ProfChatWin*)window; #ifdef HAVE_LIBOTR if (otr_is_secure(barejid)) { - ProfChatWin *chatwin = (ProfChatWin*)window; chatwin->is_otr = TRUE; } #endif @@ -1405,6 +1437,8 @@ ui_outgoing_chat_msg(const char * const from, const char * const barejid, } else { num = wins_get_num(window); } + ProfChatWin *chatwin = (ProfChatWin*)window; + chat_state_active(chatwin->state); win_save_print(window, '-', NULL, 0, THEME_TEXT_ME, from, message); ui_switch_win(num); @@ -2213,7 +2247,7 @@ ui_ask_password(void) status_bar_update_virtual(); inp_block(); inp_get_password(passwd); - inp_non_block(); + inp_non_block(prefs_get_inpblock()); return passwd; } @@ -2251,12 +2285,59 @@ ui_chat_win_contact_offline(PContact contact, char *resource, char *status) } void +ui_contact_offline(char *barejid, char *resource, char *status) +{ + char *show_console = prefs_get_string(PREF_STATUSES_CONSOLE); + char *show_chat_win = prefs_get_string(PREF_STATUSES_CHAT); + Jid *jid = jid_create_from_bare_and_resource(barejid, resource); + PContact contact = roster_get_contact(barejid); + if (p_contact_subscription(contact) != NULL) { + if (strcmp(p_contact_subscription(contact), "none") != 0) { + + // show in console if "all" + if (g_strcmp0(show_console, "all") == 0) { + cons_show_contact_offline(contact, resource, status); + + // show in console of "online" + } else if (g_strcmp0(show_console, "online") == 0) { + cons_show_contact_offline(contact, resource, status); + } + + // show in chat win if "all" + if (g_strcmp0(show_chat_win, "all") == 0) { + ui_chat_win_contact_offline(contact, resource, status); + + // show in char win if "online" and presence online + } else if (g_strcmp0(show_chat_win, "online") == 0) { + ui_chat_win_contact_offline(contact, resource, status); + } + } + } + + ProfChatWin *chatwin = wins_get_chat(barejid); + if (chatwin && chatwin->resource_override && (g_strcmp0(resource, chatwin->resource_override) == 0)) { + FREE_SET_NULL(chatwin->resource_override); + } + + prefs_free_string(show_console); + prefs_free_string(show_chat_win); + jid_destroy(jid); +} + +void ui_clear_win_title(void) { printf("%c]0;%c", '\033', '\007'); } void +ui_goodbye_title(void) +{ + int result = system("/bin/echo -ne \"\033]0;Thanks for using Profanity\007\""); + if(result == -1) log_error("Error printing title on shutdown"); +} + +void ui_statusbar_new(const int win) { status_bar_new(win); diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 4165bd3f..a6877a86 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -120,9 +120,9 @@ inp_win_resize(void) } void -inp_non_block(void) +inp_non_block(gint timeout) { - wtimeout(inp_win, prefs_get_inpblock()); + wtimeout(inp_win, timeout); } void @@ -151,17 +151,11 @@ inp_get_char(char *input, int *size, int *result) in_command = TRUE; } - if (prefs_get_boolean(PREF_STATES)) { - if (*result == ERR) { - prof_handle_idle(); - } - if (prefs_get_boolean(PREF_OUTTYPE) - && (*result != ERR) - && (*result != KEY_CODE_YES) - && !in_command - && _printable(ch)) { - prof_handle_activity(); - } + if (*result == ERR) { + prof_handle_idle(); + } + if ((*result != ERR) && (*result != KEY_CODE_YES) && !in_command && _printable(ch)) { + prof_handle_activity(); } // if it wasn't an arrow key etc diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h index eae20a51..b5a26c10 100644 --- a/src/ui/inputwin.h +++ b/src/ui/inputwin.h @@ -40,9 +40,9 @@ wint_t inp_get_char(char *input, int *size, int *result); void inp_win_reset(void); void inp_win_resize(void); void inp_put_back(void); -void inp_non_block(void); +void inp_non_block(gint); void inp_block(void); void inp_get_password(char *passwd); void inp_replace_input(char *input, const char * const new_input, int *size); -#endif \ No newline at end of file +#endif diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c index 15f8efca..326dbf8b 100644 --- a/src/ui/titlebar.c +++ b/src/ui/titlebar.c @@ -47,6 +47,7 @@ #include "ui/windows.h" #include "ui/window.h" #include "roster_list.h" +#include "chat_session.h" static WINDOW *win; static contact_presence_t current_presence; @@ -307,9 +308,17 @@ static void _show_contact_presence(ProfChatWin *chatwin) { int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET); - if (chatwin && chatwin->resource) { + char *resource = NULL; + + ChatSession *session = chat_session_get(chatwin->barejid); + if (chatwin->resource_override) { + resource = chatwin->resource_override; + } else if (session && session->resource) { + resource = session->resource; + } + if (resource && prefs_get_boolean(PREF_RESOURCE_TITLE)) { wprintw(win, "/"); - wprintw(win, chatwin->resource); + wprintw(win, resource); } if (prefs_get_boolean(PREF_PRESENCE)) { @@ -318,10 +327,10 @@ _show_contact_presence(ProfChatWin *chatwin) PContact contact = roster_get_contact(chatwin->barejid); if (contact) { - if (chatwin && chatwin->resource) { - Resource *resource = p_contact_get_resource(contact, chatwin->resource); - if (resource) { - presence = string_from_resource_presence(resource->presence); + if (resource) { + Resource *resourcep = p_contact_get_resource(contact, resource); + if (resourcep) { + presence = string_from_resource_presence(resourcep->presence); } } else { presence = p_contact_presence(contact); diff --git a/src/ui/ui.h b/src/ui/ui.h index e7b46766..e28914ff 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -115,12 +115,12 @@ char * ui_ask_password(void); void ui_handle_stanza(const char * const msg); // ui events -void ui_contact_typing(const char * const from); -void ui_incoming_msg(const char * const from, const char * const message, GTimeVal *tv_stamp); +void ui_contact_typing(const char * const barejid, const char * const resource); +void ui_incoming_msg(const char * const from, const char * const resource, const char * const message, GTimeVal *tv_stamp); void ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp); void ui_disconnected(void); -void ui_recipient_gone(const char * const barejid); +void ui_recipient_gone(const char * const barejid, const char * const resource); void ui_outgoing_chat_msg(const char * const from, const char * const barejid, const char * const message); @@ -186,10 +186,12 @@ void ui_group_added(const char * const contact, const char * const group); void ui_group_removed(const char * const contact, const char * const group); void ui_chat_win_contact_online(PContact contact, Resource *resource, GDateTime *last_activity); void ui_chat_win_contact_offline(PContact contact, char *resource, char *status); +void ui_contact_offline(char *barejid, char *resource, char *status); void ui_handle_recipient_not_found(const char * const recipient, const char * const err_msg); void ui_handle_recipient_error(const char * const recipient, const char * const err_msg); void ui_handle_error(const char * const err_msg); void ui_clear_win_title(void); +void ui_goodbye_title(void); void ui_handle_room_join_error(const char * const roomjid, const char * const err); void ui_handle_room_configuration(const char * const roomjid, DataForm *form); void ui_handle_room_configuration_form_error(const char * const roomjid, const char * const message); @@ -213,6 +215,7 @@ void ui_show_lines(ProfWin *window, const gchar** lines); void ui_redraw_all_room_rosters(void); void ui_show_all_room_rosters(void); void ui_hide_all_room_rosters(void); +gboolean ui_chat_win_exists(const char * const barejid); void ui_tidy_wins(void); void ui_prune_wins(void); @@ -229,7 +232,7 @@ void ui_statusbar_new(const int win); wint_t ui_get_char(char *input, int *size, int *result); void ui_input_clear(void); -void ui_input_nonblocking(void); +void ui_input_nonblocking(gboolean); void ui_replace_input(char *input, const char * const new_input, int *size); void ui_invalid_command_usage(const char * const usage, void (*setting_func)(void)); @@ -286,6 +289,7 @@ void cons_show_received_subs(void); void cons_show_sent_subs(void); void cons_alert(void); void cons_theme_setting(void); +void cons_resource_setting(void); void cons_privileges_setting(void); void cons_beep_setting(void); void cons_flash_setting(void); diff --git a/src/ui/window.c b/src/ui/window.c index dd459ece..3a45ab01 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -132,11 +132,12 @@ win_create_chat(const char * const barejid) new_win->window.layout = _win_create_simple_layout(); new_win->barejid = strdup(barejid); - new_win->resource = NULL; + new_win->resource_override = NULL; new_win->is_otr = FALSE; new_win->is_trusted = FALSE; new_win->history_shown = FALSE; new_win->unread = 0; + new_win->state = chat_state_new(); new_win->memcheck = PROFCHATWIN_MEMCHECK; @@ -333,7 +334,8 @@ win_free(ProfWin* window) if (window->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*)window; free(chatwin->barejid); - free(chatwin->resource); + free(chatwin->resource_override); + free(chatwin->state); } if (window->type == WIN_MUC) { @@ -738,6 +740,8 @@ win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp, buffer_push(window->layout->buffer, show_char, time, flags, theme_item, from, message); _win_print(window, show_char, time, flags, theme_item, from, message); + // TODO: cross-reference.. this should be replaced by a real event-based system + ui_input_nonblocking(TRUE); } void @@ -950,4 +954,4 @@ win_printline_nowrap(WINDOW *win, char *msg) waddnstr(win, msg, maxx); wmove(win, cury+1, 0); -} \ No newline at end of file +} diff --git a/src/ui/window.h b/src/ui/window.h index 0f04faae..b6bd0298 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -47,6 +47,7 @@ #include "muc.h" #include "ui/buffer.h" #include "xmpp/xmpp.h" +#include "chat_state.h" #define NO_ME 1 #define NO_DATE 2 @@ -109,9 +110,10 @@ typedef struct prof_chat_win_t { ProfWin window; char *barejid; int unread; + ChatState *state; gboolean is_otr; gboolean is_trusted; - char *resource; + char *resource_override; gboolean history_shown; unsigned long memcheck; } ProfChatWin; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index d87c5fb2..e96c1a74 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -80,27 +80,31 @@ message_add_handlers(void) } void -message_send_chat(const char * const barejid, const char * const resource, const char * const msg, gboolean send_state) +message_send_chat(const char * const barejid, const char * const msg) { xmpp_stanza_t *message; xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - GString *jid = g_string_new(barejid); - if (resource) { - g_string_append(jid, "/"); - g_string_append(jid, resource); - } - - if (send_state) { - message = stanza_create_message(ctx, jid->str, STANZA_TYPE_CHAT, msg, STANZA_NAME_ACTIVE); + ChatSession *session = chat_session_get(barejid); + if (session) { + char *state = NULL; + if (prefs_get_boolean(PREF_STATES) && session->send_states) { + state = STANZA_NAME_ACTIVE; + } + Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource); + message = stanza_create_message(ctx, jidp->fulljid, STANZA_TYPE_CHAT, msg, state); + jid_destroy(jidp); } else { - message = stanza_create_message(ctx, jid->str, STANZA_TYPE_CHAT, msg, NULL); + char *state = NULL; + if (prefs_get_boolean(PREF_STATES)) { + state = STANZA_NAME_ACTIVE; + } + message = stanza_create_message(ctx, barejid, STANZA_TYPE_CHAT, msg, state); } xmpp_send(conn, message); xmpp_stanza_release(message); - g_string_free(jid, TRUE); } void @@ -149,49 +153,44 @@ message_send_invite(const char * const roomjid, const char * const contact, } void -message_send_composing(const char * const barejid) +message_send_composing(const char * const jid) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, barejid, - STANZA_NAME_COMPOSING); + xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_COMPOSING); xmpp_send(conn, stanza); xmpp_stanza_release(stanza); + } void -message_send_paused(const char * const barejid) +message_send_paused(const char * const jid) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, barejid, - STANZA_NAME_PAUSED); - + xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_PAUSED); xmpp_send(conn, stanza); xmpp_stanza_release(stanza); } void -message_send_inactive(const char * const barejid) +message_send_inactive(const char * const jid) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, barejid, - STANZA_NAME_INACTIVE); + xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_INACTIVE); xmpp_send(conn, stanza); xmpp_stanza_release(stanza); } void -message_send_gone(const char * const barejid) +message_send_gone(const char * const jid) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, barejid, - STANZA_NAME_GONE); - + xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_GONE); xmpp_send(conn, stanza); xmpp_stanza_release(stanza); } @@ -462,34 +461,10 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, // standard chat message, use jid without resource } else { - // determine chatstate support of recipient - gboolean recipient_supports = FALSE; - if (stanza_contains_chat_state(stanza)) { - recipient_supports = TRUE; - } - - // create or update chat session - chat_session_on_incoming_message(jid->barejid, recipient_supports); - // determine if the notifications happened whilst offline GTimeVal tv_stamp; gboolean delayed = stanza_get_delay(stanza, &tv_stamp); - // deal with chat states if recipient supports them - if (recipient_supports && (!delayed)) { - if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL) { - handle_typing(jid->barejid); - } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL) { - handle_gone(jid->barejid); - } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL) { - // do something - } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL) { - // do something - } else { // handle <active/> - // do something - } - } - // check for and deal with message xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY); if (body != NULL) { @@ -498,12 +473,33 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, if (delayed) { handle_delayed_message(jid->barejid, message, tv_stamp); } else { - handle_incoming_message(jid->barejid, message); + handle_incoming_message(jid->barejid, jid->resourcepart, message); } xmpp_free(ctx, message); } } + // handle chat sessions and states + if (!delayed && jid->resourcepart) { + gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL; + gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL; + gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL; + gboolean inactive = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL; + if (gone) { + handle_gone(jid->barejid, jid->resourcepart); + } else if (typing) { + handle_typing(jid->barejid, jid->resourcepart); + } else if (paused) { + handle_paused(jid->barejid, jid->resourcepart); + } else if (inactive) { + handle_inactive(jid->barejid, jid->resourcepart); + } else if (stanza_contains_chat_state(stanza)) { + handle_activity(jid->barejid, jid->resourcepart, TRUE); + } else { + handle_activity(jid->barejid, jid->resourcepart, FALSE); + } + } + jid_destroy(jid); return 1; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 1766fe26..4f1d412d 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -199,15 +199,14 @@ stanza_create_bookmarks_pubsub_add(xmpp_ctx_t *ctx, const char * const jid, #endif xmpp_stanza_t * -stanza_create_chat_state(xmpp_ctx_t *ctx, const char * const recipient, - const char * const state) +stanza_create_chat_state(xmpp_ctx_t *ctx, const char * const fulljid, const char * const state) { xmpp_stanza_t *msg, *chat_state; msg = xmpp_stanza_new(ctx); xmpp_stanza_set_name(msg, STANZA_NAME_MESSAGE); xmpp_stanza_set_type(msg, STANZA_TYPE_CHAT); - xmpp_stanza_set_attribute(msg, STANZA_ATTR_TO, recipient); + xmpp_stanza_set_attribute(msg, STANZA_ATTR_TO, fulljid); char *id = create_unique_id(NULL); xmpp_stanza_set_id(msg, id); free(id); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index e60e9fe1..84282401 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -179,7 +179,7 @@ typedef enum { xmpp_stanza_t* stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx); xmpp_stanza_t* stanza_create_chat_state(xmpp_ctx_t *ctx, - const char * const recipient, const char * const state); + const char * const fulljid, const char * const state); xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient, const char * const type, diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 2499d008..161eebdf 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -145,16 +145,15 @@ char* jabber_get_account_name(void); GList * jabber_get_available_resources(void); // message functions -void message_send_chat(const char * const barejid, const char * const resource, const char * const msg, - gboolean send_state); +void message_send_chat(const char * const barejid, const char * const msg); void message_send_private(const char * const fulljid, const char * const msg); void message_send_groupchat(const char * const roomjid, const char * const msg); void message_send_groupchat_subject(const char * const roomjid, const char * const subject); -void message_send_inactive(const char * const barejid); -void message_send_composing(const char * const barejid); -void message_send_paused(const char * const barejid); -void message_send_gone(const char * const barejid); +void message_send_inactive(const char * const jid); +void message_send_composing(const char * const jid); +void message_send_paused(const char * const jid); +void message_send_gone(const char * const jid); void message_send_invite(const char * const room, const char * const contact, const char * const reason); diff --git a/tests/helpers.c b/tests/helpers.c index a6a473e4..10310886 100644 --- a/tests/helpers.c +++ b/tests/helpers.c @@ -10,6 +10,7 @@ #include "common.h" #include "helpers.h" #include "config/preferences.h" +#include "chat_session.h" void create_config_dir(void **state) { @@ -72,6 +73,18 @@ void close_preferences(void **state) rmdir("./tests/files"); } +void init_chat_sessions(void **state) +{ + load_preferences(NULL); + chat_sessions_init(); +} + +void close_chat_sessions(void **state) +{ + chat_sessions_clear(); + close_preferences(NULL); +} + static GCompareFunc cmp_func; void diff --git a/tests/helpers.h b/tests/helpers.h index 17d8329c..2d7af6e7 100644 --- a/tests/helpers.h +++ b/tests/helpers.h @@ -3,5 +3,8 @@ void load_preferences(void **state); void close_preferences(void **state); +void init_chat_sessions(void **state); +void close_chat_sessions(void **state); + void glist_set_cmp(GCompareFunc func); int glist_contents_equal(const void *actual, const void *expected); \ No newline at end of file diff --git a/tests/test_chat_session.c b/tests/test_chat_session.c new file mode 100644 index 00000000..b5e1f7b6 --- /dev/null +++ b/tests/test_chat_session.c @@ -0,0 +1,51 @@ +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <stdlib.h> + +#include "chat_session.h" + +void returns_false_when_chat_session_does_not_exist(void **state) +{ + ChatSession *session = chat_session_get("somejid@server.org"); + assert_null(session); +} + +void creates_chat_session_on_recipient_activity(void **state) +{ + char *barejid = "myjid@server.org"; + char *resource = "tablet"; + + chat_session_recipient_active(barejid, resource, FALSE); + ChatSession *session = chat_session_get(barejid); + + assert_non_null(session); + assert_string_equal(session->resource, resource); +} + +void replaces_chat_session_on_recipient_activity_with_different_resource(void **state) +{ + char *barejid = "myjid@server.org"; + char *resource1 = "tablet"; + char *resource2 = "mobile"; + + chat_session_recipient_active(barejid, resource1, FALSE); + chat_session_recipient_active(barejid, resource2, FALSE); + ChatSession *session = chat_session_get(barejid); + + assert_string_equal(session->resource, resource2); +} + +void removes_chat_session(void **state) +{ + char *barejid = "myjid@server.org"; + char *resource1 = "laptop"; + + chat_session_recipient_active(barejid, resource1, FALSE); + chat_session_remove(barejid); + ChatSession *session = chat_session_get(barejid); + + assert_null(session); +} \ No newline at end of file diff --git a/tests/test_chat_session.h b/tests/test_chat_session.h new file mode 100644 index 00000000..4ce03fd5 --- /dev/null +++ b/tests/test_chat_session.h @@ -0,0 +1,4 @@ +void returns_false_when_chat_session_does_not_exist(void **state); +void creates_chat_session_on_recipient_activity(void **state); +void replaces_chat_session_on_recipient_activity_with_different_resource(void **state); +void removes_chat_session(void **state); \ No newline at end of file diff --git a/tests/test_cmd_disconnect.c b/tests/test_cmd_disconnect.c new file mode 100644 index 00000000..68253820 --- /dev/null +++ b/tests/test_cmd_disconnect.c @@ -0,0 +1,37 @@ +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <stdlib.h> +#include <string.h> + +#include "chat_session.h" +#include "command/commands.h" +#include "xmpp/xmpp.h" +#include "roster_list.h" + +#include "ui/stub_ui.h" + +void clears_chat_sessions(void **state) +{ + CommandHelp *help = malloc(sizeof(CommandHelp)); + + chat_sessions_init(); + roster_init(); + chat_session_recipient_active("bob@server.org", "laptop", FALSE); + chat_session_recipient_active("mike@server.org", "work", FALSE); + + will_return(jabber_get_connection_status, JABBER_CONNECTED); + will_return(jabber_get_fulljid, "myjid@myserver.com"); + expect_any_cons_show(); + + gboolean result = cmd_disconnect(NULL, *help); + + assert_true(result); + + ChatSession *session1 = chat_session_get("bob@server.org"); + ChatSession *session2 = chat_session_get("mike@server.org"); + assert_null(session1); + assert_null(session2); + free(help); +} \ No newline at end of file diff --git a/tests/test_cmd_disconnect.h b/tests/test_cmd_disconnect.h new file mode 100644 index 00000000..856b501e --- /dev/null +++ b/tests/test_cmd_disconnect.h @@ -0,0 +1 @@ +void clears_chat_sessions(void **state); diff --git a/tests/test_cmd_otr.c b/tests/test_cmd_otr.c index 470615e6..c6c6f7cf 100644 --- a/tests/test_cmd_otr.c +++ b/tests/test_cmd_otr.c @@ -552,9 +552,7 @@ cmd_otr_start_sends_otr_query_message_to_current_recipeint(void **state) will_return(otr_start_query, query_message); expect_string(message_send_chat, barejid, chatwin->barejid); - expect_value(message_send_chat, resource, NULL); expect_string(message_send_chat, msg, query_message); - expect_any(message_send_chat, send_state); gboolean result = cmd_otr(args, *help); assert_true(result); diff --git a/tests/test_server_events.c b/tests/test_server_events.c index 435493a2..ce54b4f2 100644 --- a/tests/test_server_events.c +++ b/tests/test_server_events.c @@ -11,6 +11,7 @@ #include "chat_session.h" #include "config/preferences.h" #include "ui/ui.h" +#include "ui/stub_ui.h" #include "muc.h" void console_doesnt_show_online_presence_when_set_none(void **state) @@ -120,29 +121,24 @@ void handle_message_error_when_recipient_cancel(void **state) prefs_set_boolean(PREF_STATES, FALSE); chat_sessions_init(); - expect_string(ui_handle_recipient_not_found, recipient, from); - expect_string(ui_handle_recipient_not_found, err_msg, err_msg); - handle_message_error(from, type, err_msg); } void handle_message_error_when_recipient_cancel_disables_chat_session(void **state) { char *err_msg = "Some error."; - char *from = "bob@server.com"; + char *barejid = "bob@server.com"; + char *resource = "resource"; char *type = "cancel"; prefs_set_boolean(PREF_STATES, TRUE); chat_sessions_init(); - chat_session_on_incoming_message(from, TRUE); + chat_session_recipient_active(barejid, resource, FALSE); - expect_any(ui_handle_recipient_not_found, recipient); - expect_any(ui_handle_recipient_not_found, err_msg); + handle_message_error(barejid, type, err_msg); + ChatSession *session = chat_session_get(barejid); - handle_message_error(from, type, err_msg); - gboolean chat_session_supported = chat_session_on_message_send(from); - - assert_false(chat_session_supported); + assert_null(session); chat_sessions_clear(); } @@ -180,3 +176,37 @@ void handle_presence_error_when_from_recipient(void **state) handle_presence_error(from, type, err_msg); } + +void handle_offline_removes_chat_session(void **state) +{ + chat_sessions_init(); + char *barejid = "friend@server.chat.com"; + char *resource = "home"; + roster_init(); + roster_add(barejid, "bob", NULL, "both", FALSE); + Resource *resourcep = resource_new(resource, RESOURCE_ONLINE, NULL, 10); + roster_update_presence(barejid, resourcep, NULL); + chat_session_recipient_active(barejid, resource, FALSE); + handle_contact_offline(barejid, resource, NULL); + ChatSession *session = chat_session_get(barejid); + + assert_null(session); + + roster_clear(); + chat_sessions_clear(); +} + +void lost_connection_clears_chat_sessions(void **state) +{ + chat_sessions_init(); + chat_session_recipient_active("bob@server.org", "laptop", FALSE); + chat_session_recipient_active("steve@server.org", "mobile", FALSE); + expect_any_cons_show_error(); + + handle_lost_connection(); + + ChatSession *session1 = chat_session_get("bob@server.org"); + ChatSession *session2 = chat_session_get("steve@server.org"); + assert_null(session1); + assert_null(session2); +} diff --git a/tests/test_server_events.h b/tests/test_server_events.h index 68e78557..81a436f4 100644 --- a/tests/test_server_events.h +++ b/tests/test_server_events.h @@ -9,4 +9,6 @@ void handle_message_error_when_recipient_cancel(void **state); void handle_message_error_when_recipient_cancel_disables_chat_session(void **state); void handle_message_error_when_recipient_and_no_type(void **state); void handle_presence_error_when_no_recipient(void **state); -void handle_presence_error_when_from_recipient(void **state); \ No newline at end of file +void handle_presence_error_when_from_recipient(void **state); +void handle_offline_removes_chat_session(void **state); +void lost_connection_clears_chat_sessions(void **state); diff --git a/tests/testsuite.c b/tests/testsuite.c index fb439331..eb039cbe 100644 --- a/tests/testsuite.c +++ b/tests/testsuite.c @@ -8,9 +8,11 @@ #include <sys/stat.h> #include "config.h" +#include "chat_session.h" #include "helpers.h" #include "test_autocomplete.h" +#include "test_chat_session.h" #include "test_common.h" #include "test_contact.h" #include "test_cmd_connect.h" @@ -31,6 +33,7 @@ #include "test_muc.h" #include "test_cmd_roster.h" #include "test_cmd_win.h" +#include "test_cmd_disconnect.h" #include "test_form.h" int main(int argc, char* argv[]) { @@ -204,6 +207,18 @@ int main(int argc, char* argv[]) { unit_test(find_five_times_finds_fifth), unit_test(find_twice_returns_first_when_two_match_and_reset), + unit_test_setup_teardown(returns_false_when_chat_session_does_not_exist, + init_chat_sessions, + close_chat_sessions), + unit_test_setup_teardown(creates_chat_session_on_recipient_activity, + init_chat_sessions, + close_chat_sessions), + unit_test_setup_teardown(replaces_chat_session_on_recipient_activity_with_different_resource, + init_chat_sessions, + close_chat_sessions), + unit_test_setup_teardown(removes_chat_session, + init_chat_sessions, + close_chat_sessions), unit_test_setup_teardown(cmd_connect_shows_message_when_disconnecting, load_preferences, close_preferences), @@ -439,6 +454,8 @@ int main(int argc, char* argv[]) { unit_test(handle_message_error_when_recipient_and_no_type), unit_test(handle_presence_error_when_no_recipient), unit_test(handle_presence_error_when_from_recipient), + unit_test(handle_offline_removes_chat_session), + unit_test(lost_connection_clears_chat_sessions), unit_test(cmd_alias_add_shows_usage_when_no_args), unit_test(cmd_alias_add_shows_usage_when_no_value), @@ -594,6 +611,8 @@ int main(int argc, char* argv[]) { unit_test(remove_text_multi_value_does_nothing_when_doesnt_exist), unit_test(remove_text_multi_value_removes_when_one), unit_test(remove_text_multi_value_removes_when_many), + + unit_test(clears_chat_sessions), }; return run_tests(all_tests); diff --git a/tests/ui/stub_ui.c b/tests/ui/stub_ui.c index 5361c08d..76b71265 100644 --- a/tests/ui/stub_ui.c +++ b/tests/ui/stub_ui.c @@ -20,12 +20,24 @@ expect_cons_show(char *expected) } void +expect_any_cons_show(void) +{ + expect_any(cons_show, output); +} + +void expect_cons_show_error(char *expected) { expect_string(cons_show_error, output, expected); } void +expect_any_cons_show_error(void) +{ + expect_any(cons_show_error, output); +} + +void expect_ui_current_print_line(char *message) { expect_string(ui_current_print_line, output, message); @@ -176,12 +188,12 @@ char * ui_ask_password(void) void ui_handle_stanza(const char * const msg) {} // ui events -void ui_contact_typing(const char * const from) {} -void ui_incoming_msg(const char * const from, const char * const message, GTimeVal *tv_stamp) {} +void ui_contact_typing(const char * const barejid, const char * const resource) {} +void ui_incoming_msg(const char * const from, const char * const resource, const char * const message, GTimeVal *tv_stamp) {} void ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp) {} void ui_disconnected(void) {} -void ui_recipient_gone(const char * const barejid) {} +void ui_recipient_gone(const char * const barejid, const char * const resource) {} void ui_outgoing_chat_msg(const char * const from, const char * const barejid, const char * const message) {} @@ -247,6 +259,12 @@ void ui_group_added(const char * const contact, const char * const group) {} void ui_group_removed(const char * const contact, const char * const group) {} void ui_chat_win_contact_online(PContact contact, Resource *resource, GDateTime *last_activity) {} void ui_chat_win_contact_offline(PContact contact, char *resource, char *status) {} +gboolean ui_chat_win_exists(const char * const barejid) +{ + return TRUE; +} + +void ui_contact_offline(char *barejid, char *resource, char *status) {} void ui_handle_recipient_not_found(const char * const recipient, const char * const err_msg) { @@ -266,6 +284,7 @@ void ui_handle_error(const char * const err_msg) } void ui_clear_win_title(void) {} +void ui_goodbye_title(void) {} void ui_handle_room_join_error(const char * const roomjid, const char * const err) {} void ui_handle_room_configuration(const char * const roomjid, DataForm *form) {} void ui_handle_room_configuration_form_error(const char * const roomjid, const char * const message) {} @@ -312,7 +331,7 @@ wint_t ui_get_char(char *input, int *size, int *result) } void ui_input_clear(void) {} -void ui_input_nonblocking(void) {} +void ui_input_nonblocking(gboolean reset) {} void ui_replace_input(char *input, const char * const new_input, int *size) {} void ui_invalid_command_usage(const char * const usage, void (*setting_func)(void)) {} @@ -424,6 +443,7 @@ void cons_beep_setting(void) {} void cons_flash_setting(void) {} void cons_splash_setting(void) {} void cons_vercheck_setting(void) {} +void cons_resource_setting(void) {} void cons_occupants_setting(void) {} void cons_roster_setting(void) {} void cons_presence_setting(void) {} diff --git a/tests/ui/stub_ui.h b/tests/ui/stub_ui.h index f64eba02..81357a86 100644 --- a/tests/ui/stub_ui.h +++ b/tests/ui/stub_ui.h @@ -1,4 +1,6 @@ void expect_cons_show(char *expected); +void expect_any_cons_show(void); void expect_cons_show_error(char *expected); +void expect_any_cons_show_error(void); void expect_ui_current_print_line(char *message); void expect_ui_current_print_formatted_line(char show_char, int attrs, char *message); \ No newline at end of file diff --git a/tests/xmpp/stub_xmpp.c b/tests/xmpp/stub_xmpp.c index 580a6c61..cc5ad5fc 100644 --- a/tests/xmpp/stub_xmpp.c +++ b/tests/xmpp/stub_xmpp.c @@ -29,7 +29,7 @@ void jabber_shutdown(void) {} void jabber_process_events(void) {} const char * jabber_get_fulljid(void) { - return NULL; + return (char *)mock(); } const char * jabber_get_domain(void) @@ -58,13 +58,10 @@ GList * jabber_get_available_resources(void) } // message functions -void message_send_chat(const char * const barejid, const char * const resource, const char * const msg, - gboolean send_state) +void message_send_chat(const char * const barejid, const char * const msg) { check_expected(barejid); - check_expected(resource); check_expected(msg); - check_expected(send_state); } void message_send_private(const char * const fulljid, const char * const msg) {} @@ -218,4 +215,4 @@ void roster_send_add_new(const char * const barejid, const char * const name) void roster_send_remove(const char * const barejid) { check_expected(barejid); -} \ No newline at end of file +} |