1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-06-23 21:45:30 +00:00
profanity/src/event/server_events.c
Steffen Jaeckel 7d9c3c1b32 fix "window NULL issue" (hopefully)
There were multiple reports where after a reconnect the window of the
MUC that was last opened, was empty.

`muc_join()` creates an instance of a MUC, `presence_join_room()` works
with this instance. Therefore the instance has to exist before working on
it.

I'm not sure if this really fixes the issue, but at least it didn't
happen anymore after I applied this modification.
I can't remember how I stumbled over this, either while looking at debug
logs or while looking at Valgrind output while a reconnect happened, but
something went wrong. Then I came to the conclusion that this may fix
the issue and for now it did ... maybe it comes back, then my RCA was
wrong.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-05-10 17:51:52 +02:00

1362 lines
40 KiB
C

/*
* server_events.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2023 Michael Vetter <jubalh@iodoru.org>
*
* 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 <https://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 "config.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "config/accounts.h"
#include "profanity.h"
#include "log.h"
#include "chatlog.h"
#include "database.h"
#include "config/preferences.h"
#include "config/tlscerts.h"
#include "config/account.h"
#include "config/cafile.h"
#include "config/scripts.h"
#include "event/client_events.h"
#include "event/server_events.h"
#include "event/common.h"
#include "plugins/plugins.h"
#include "ui/window_list.h"
#include "ui/window.h"
#include "tools/bookmark_ignore.h"
#include "xmpp/xmpp.h"
#include "xmpp/muc.h"
#include "xmpp/chat_session.h"
#include "xmpp/roster_list.h"
#include "xmpp/avatar.h"
#include "xmpp/vcard_funcs.h"
#ifdef HAVE_LIBOTR
#include "otr/otr.h"
#endif
#ifdef HAVE_LIBGPGME
#include "pgp/gpg.h"
#endif
#ifdef HAVE_OMEMO
#include "omemo/omemo.h"
#endif
#include "ui/ui.h"
static void _clean_incoming_message(ProfMessage* message);
static void _sv_ev_incoming_plain(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message, gboolean logit);
void
sv_ev_login_account_success(char* account_name, gboolean secured)
{
ProfAccount* account = accounts_get_account(account_name);
bookmark_ignore_on_connect(account->jid);
roster_create();
#ifdef HAVE_LIBOTR
otr_on_connect(account);
#endif
#ifdef HAVE_LIBGPGME
p_gpg_on_connect(account->jid);
#endif
#ifdef HAVE_OMEMO
omemo_on_connect(account);
#endif
log_database_init(account);
vcard_user_refresh();
avatar_pep_subscribe();
ui_handle_login_account_success(account, secured);
// attempt to rejoin all rooms
GList* rooms = muc_rooms();
GList* curr = rooms;
while (curr) {
char* password = muc_password(curr->data);
char* nick = muc_nick(curr->data);
presence_join_room(curr->data, nick, password);
curr = g_list_next(curr);
}
g_list_free(rooms);
log_info("%s logged in successfully", account->jid);
// if we have been connected before
if (ev_was_connected_already()) {
cons_show("Connection re-established.");
wins_reestablished_connection();
}
ev_inc_connection_counter();
if (account->startscript) {
scripts_exec(account->startscript);
}
account_free(account);
}
void
sv_ev_roster_received(void)
{
roster_process_pending_presence();
if (prefs_get_boolean(PREF_ROSTER)) {
ui_show_roster();
}
char* account_name = session_get_account_name();
#ifdef HAVE_LIBGPGME
// check pgp key valid if specified
ProfAccount* account = accounts_get_account(account_name);
if (account && account->pgp_keyid) {
char* err_str = NULL;
if (!p_gpg_valid_key(account->pgp_keyid, &err_str)) {
cons_show_error("Invalid PGP key ID specified: %s, %s", account->pgp_keyid, err_str);
}
free(err_str);
// Redraw the screen after entry of the PGP secret key, but not init
ProfWin* win = wins_get_current();
char* theme = prefs_get_string(PREF_THEME);
win_redraw(win);
theme_init(theme);
g_free(theme);
ui_resize();
ui_show_roster();
}
account_free(account);
#endif
// send initial presence
resource_presence_t conn_presence = accounts_get_login_presence(account_name);
char* last_activity_str = accounts_get_last_activity(account_name);
char* status_message = accounts_get_login_status(account_name);
int diff_secs = 0;
if (prefs_get_boolean(PREF_LASTACTIVITY) && last_activity_str) {
GTimeVal lasttv;
GDateTime* nowdt = g_date_time_new_now_utc();
gboolean res = g_time_val_from_iso8601(last_activity_str, &lasttv);
if (res) {
GDateTime* lastdt = g_date_time_new_from_timeval_utc(&lasttv);
GTimeSpan diff_micros = g_date_time_difference(nowdt, lastdt);
diff_secs = (diff_micros / 1000) / 1000;
g_date_time_unref(lastdt);
}
g_date_time_unref(nowdt);
}
connection_set_presence_msg(status_message);
cl_ev_presence_send(conn_presence, diff_secs);
g_free(status_message);
g_free(last_activity_str);
const char* fulljid = connection_get_fulljid();
plugins_on_connect(account_name, fulljid);
}
void
sv_ev_connection_features_received(void)
{
#ifdef HAVE_OMEMO
omemo_publish_crypto_materials();
#endif
}
void
sv_ev_lost_connection(void)
{
cons_show_error("Lost connection.");
#ifdef HAVE_LIBOTR
GSList* recipients = wins_get_chat_recipients();
GSList* curr = recipients;
while (curr) {
char* barejid = curr->data;
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin && otr_is_secure(barejid)) {
chatwin_otr_unsecured(chatwin);
otr_end_session(barejid);
}
curr = g_slist_next(curr);
}
if (recipients) {
g_slist_free(recipients);
}
#endif
ev_disconnect_cleanup();
}
void
sv_ev_failed_login(void)
{
cons_show_error("Login failed.");
log_info("Login failed");
tlscerts_clear_current();
}
void
sv_ev_room_invite(jabber_invite_t invite_type, const char* const invitor, const char* const room,
const char* const reason, const char* const password)
{
if (!muc_active(room) && !muc_invites_contain(room)) {
cons_show_room_invite(invitor, room, reason);
muc_invites_add(room, password);
}
}
void
sv_ev_room_broadcast(const char* const room_jid, const char* const message)
{
if (muc_roster_complete(room_jid)) {
ProfMucWin* mucwin = wins_get_muc(room_jid);
if (mucwin) {
mucwin_broadcast(mucwin, message);
}
} else {
muc_pending_broadcasts_add(room_jid, message);
}
}
void
sv_ev_room_subject(const char* const room, const char* const nick, const char* const subject)
{
muc_set_subject(room, subject);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && muc_roster_complete(room) && ev_is_first_connect()) {
mucwin_subject(mucwin, nick, subject);
}
}
void
sv_ev_room_history(ProfMessage* message)
{
if (prefs_get_boolean(PREF_NOTIFY_ROOM_OFFLINE)) {
// check if this message was sent while we were offline.
// if so, treat it as a new message rather than a history event.
char* account_name = session_get_account_name();
char* last_activity = accounts_get_last_activity(account_name);
int msg_is_new = 0;
if (last_activity) {
GTimeVal lasttv;
if (g_time_val_from_iso8601(last_activity, &lasttv)) {
GDateTime* lastdt = g_date_time_new_from_timeval_utc(&lasttv);
GDateTime* msgdt = message->timestamp;
GTimeSpan time_diff = g_date_time_difference(msgdt, lastdt);
msg_is_new = (time_diff > 0);
g_date_time_unref(lastdt);
}
g_free(last_activity);
if (msg_is_new) {
sv_ev_room_message(message);
return;
}
}
}
ProfMucWin* mucwin = wins_get_muc(message->from_jid->barejid);
if (mucwin) {
// if this is the first successful connection
if (ev_is_first_connect()) {
// save timestamp of last received muc message
// so we dont display, if there was no activity in channel, once we reconnect
if (mucwin->last_msg_timestamp) {
g_date_time_unref(mucwin->last_msg_timestamp);
}
mucwin->last_msg_timestamp = g_date_time_new_now_local();
}
gboolean younger = g_date_time_compare(mucwin->last_msg_timestamp, message->timestamp) < 0 ? TRUE : FALSE;
if (ev_is_first_connect() || younger) {
mucwin_history(mucwin, message);
}
}
}
static void
_log_muc(ProfMessage* message)
{
if (message->enc == PROF_MSG_ENC_OMEMO) {
groupchat_log_omemo_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain);
} else {
groupchat_log_msg_in(message->from_jid->barejid, message->from_jid->resourcepart, message->plain);
}
log_database_add_incoming(message);
}
void
sv_ev_room_message(ProfMessage* message)
{
ProfMucWin* mucwin = wins_get_muc(message->from_jid->barejid);
if (!mucwin) {
return;
}
char* mynick = muc_nick(mucwin->roomjid);
// only log message not coming from this client (but maybe same account, different client)
// our messages are logged when outgoing
if (!(g_strcmp0(mynick, message->from_jid->resourcepart) == 0 && message_is_sent_by_us(message, TRUE))) {
_log_muc(message);
}
char* old_plain = message->plain;
message->plain = plugins_pre_room_message_display(message->from_jid->barejid, message->from_jid->resourcepart, message->plain);
GSList* mentions = get_mentions(prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD), prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE), message->plain, mynick);
gboolean mention = g_slist_length(mentions) > 0;
GList* triggers = prefs_message_get_triggers(message->plain);
_clean_incoming_message(message);
mucwin_incoming_msg(mucwin, message, mentions, triggers, TRUE);
g_slist_free(mentions);
ProfWin* window = (ProfWin*)mucwin;
int num = wins_get_num(window);
gboolean is_current = FALSE;
// currently in groupchat window
if (wins_is_current(window)) {
is_current = TRUE;
status_bar_active(num, WIN_MUC, mucwin->roomjid);
if ((g_strcmp0(mynick, message->from_jid->resourcepart) != 0) && (prefs_get_boolean(PREF_BEEP))) {
beep();
}
// not currently on groupchat window
} else {
status_bar_new(num, WIN_MUC, mucwin->roomjid);
if ((g_strcmp0(mynick, message->from_jid->resourcepart) != 0) && (prefs_get_boolean(PREF_FLASH))) {
flash();
}
cons_show_incoming_room_message(message->from_jid->resourcepart, mucwin->roomjid, num, mention, triggers, mucwin->unread, window);
mucwin->unread++;
if (mention) {
mucwin->unread_mentions = TRUE;
}
if (triggers) {
mucwin->unread_triggers = TRUE;
}
}
// save timestamp of last received muc message
if (mucwin->last_msg_timestamp) {
g_date_time_unref(mucwin->last_msg_timestamp);
}
mucwin->last_msg_timestamp = g_date_time_new_now_local();
if (prefs_do_room_notify(is_current, mucwin->roomjid, mynick, message->from_jid->resourcepart, message->plain, mention, triggers != NULL)) {
Jid* jidp = jid_create(mucwin->roomjid);
if (jidp) {
notify_room_message(message->from_jid->resourcepart, jidp->localpart, num, message->plain);
jid_destroy(jidp);
}
}
if (triggers) {
g_list_free_full(triggers, free);
}
rosterwin_roster();
plugins_post_room_message_display(message->from_jid->barejid, message->from_jid->resourcepart, message->plain);
free(message->plain);
message->plain = old_plain;
}
void
sv_ev_incoming_private_message(ProfMessage* message)
{
char* old_plain = message->plain;
message->plain = plugins_pre_priv_message_display(message->from_jid->fulljid, message->plain);
ProfPrivateWin* privatewin = wins_get_private(message->from_jid->fulljid);
if (privatewin == NULL) {
ProfWin* window = wins_new_private(message->from_jid->fulljid);
privatewin = (ProfPrivateWin*)window;
}
_clean_incoming_message(message);
privwin_incoming_msg(privatewin, message);
// Intentionally skipping log to DB because we can't authenticate the sender
chat_log_msg_in(message);
plugins_post_priv_message_display(message->from_jid->fulljid, message->plain);
free(message->plain);
message->plain = old_plain;
rosterwin_roster();
}
void
sv_ev_delayed_private_message(ProfMessage* message)
{
char* old_plain = message->plain;
message->plain = plugins_pre_priv_message_display(message->from_jid->fulljid, message->plain);
ProfPrivateWin* privatewin = wins_get_private(message->from_jid->fulljid);
if (privatewin == NULL) {
ProfWin* window = wins_new_private(message->from_jid->fulljid);
privatewin = (ProfPrivateWin*)window;
}
_clean_incoming_message(message);
privwin_incoming_msg(privatewin, message);
// Intentionally skipping log to DB because we can't authenticate the sender
chat_log_msg_in(message);
plugins_post_priv_message_display(message->from_jid->fulljid, message->plain);
free(message->plain);
message->plain = old_plain;
}
void
sv_ev_outgoing_carbon(ProfMessage* message)
{
ProfChatWin* chatwin = wins_get_chat(message->to_jid->barejid);
if (!chatwin) {
chatwin = chatwin_new(message->to_jid->barejid);
}
chat_state_active(chatwin->state);
if (message->enc == PROF_MSG_ENC_OMEMO) {
chatwin_outgoing_carbon(chatwin, message);
} else if (message->encrypted) {
#ifdef HAVE_LIBGPGME
message->plain = p_gpg_decrypt(message->encrypted);
if (message->plain) {
message->enc = PROF_MSG_ENC_PGP;
chatwin_outgoing_carbon(chatwin, message);
} else {
if (!message->body) {
log_error("Couldn't decrypt GPG message and body was empty");
return;
}
message->enc = PROF_MSG_ENC_NONE;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
#endif
} else {
message->enc = PROF_MSG_ENC_NONE;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
if (message->plain) {
if (message->type == PROF_MSG_TYPE_MUCPM) {
// MUC PM, should have resource (nick) in filename
chat_log_msg_out(message->to_jid->barejid, message->plain, message->from_jid->resourcepart);
} else {
chat_log_msg_out(message->to_jid->barejid, message->plain, NULL);
}
log_database_add_incoming(message);
}
return;
}
static void
_sv_ev_incoming_pgp(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message, gboolean logit)
{
#ifdef HAVE_LIBGPGME
message->plain = p_gpg_decrypt(message->encrypted);
if (message->plain) {
message->enc = PROF_MSG_ENC_PGP;
_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
if (logit) {
chat_log_pgp_msg_in(message);
}
chatwin->pgp_recv = TRUE;
p_gpg_free_decrypted(message->plain);
message->plain = NULL;
} else {
if (!message->body) {
log_error("Couldn't decrypt GPG message and body was empty");
return;
}
message->enc = PROF_MSG_ENC_NONE;
message->plain = strdup(message->body);
_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
chat_log_msg_in(message);
chatwin->pgp_recv = FALSE;
}
#endif
}
static void
_sv_ev_incoming_ox(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message, gboolean logit)
{
#ifdef HAVE_LIBGPGME
if (message->plain == NULL) {
if (message->body == NULL) {
log_error("Couldn't decrypt OX message and body was empty");
return;
}
message->plain = strdup(message->body);
}
//_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
if (logit) {
chat_log_pgp_msg_in(message);
}
chatwin->pgp_recv = TRUE;
// p_gpg_free_decrypted(message->plain);
message->plain = NULL;
#endif
}
static void
_sv_ev_incoming_otr(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message)
{
#ifdef HAVE_LIBOTR
gboolean decrypted = FALSE;
message->plain = otr_on_message_recv(message->from_jid->barejid, message->from_jid->resourcepart, message->body, &decrypted);
if (message->plain) {
if (decrypted) {
message->enc = PROF_MSG_ENC_OTR;
chatwin->pgp_send = FALSE;
} else {
message->enc = PROF_MSG_ENC_NONE;
}
_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
chat_log_otr_msg_in(message);
otr_free_message(message->plain);
message->plain = NULL;
chatwin->pgp_recv = FALSE;
}
#else
_sv_ev_incoming_plain(chatwin, new_win, message, TRUE);
#endif
}
static void
_sv_ev_incoming_omemo(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message, gboolean logit)
{
#ifdef HAVE_OMEMO
_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
if (logit) {
chat_log_omemo_msg_in(message);
}
chatwin->pgp_recv = FALSE;
#endif
}
static void
_sv_ev_incoming_plain(ProfChatWin* chatwin, gboolean new_win, ProfMessage* message, gboolean logit)
{
if (message->body) {
message->enc = PROF_MSG_ENC_NONE;
message->plain = strdup(message->body);
_clean_incoming_message(message);
chatwin_incoming_msg(chatwin, message, new_win);
log_database_add_incoming(message);
if (logit) {
chat_log_msg_in(message);
}
chatwin->pgp_recv = FALSE;
}
}
void
sv_ev_incoming_message(ProfMessage* message)
{
gboolean new_win = FALSE;
ProfChatWin* chatwin;
char* looking_for_jid = message->from_jid->barejid;
if (message->is_mam) {
char* mybarejid = connection_get_barejid();
if (g_strcmp0(mybarejid, message->from_jid->barejid) == 0) {
if (message->to_jid) {
looking_for_jid = message->to_jid->barejid;
}
}
free(mybarejid);
}
chatwin = wins_get_chat(looking_for_jid);
if (!chatwin) {
chatwin = chatwin_new(looking_for_jid);
ProfWin* window = (ProfWin*)chatwin;
new_win = TRUE;
if (prefs_get_boolean(PREF_MAM)) {
win_print_loading_history(window);
iq_mam_request(chatwin, g_date_time_add_seconds(message->timestamp, 0)); // copy timestamp
}
#ifdef HAVE_OMEMO
if (!message->is_mam) {
if (omemo_automatic_start(message->from_jid->barejid)) {
omemo_start_session(message->from_jid->barejid);
chatwin->is_omemo = TRUE;
}
}
#endif
}
if (message->enc == PROF_MSG_ENC_OX) {
_sv_ev_incoming_ox(chatwin, new_win, message, TRUE);
} else if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message, TRUE);
} else if (message->encrypted) {
if (chatwin->is_otr) {
win_println((ProfWin*)chatwin, THEME_DEFAULT, "-", "PGP encrypted message received whilst in OTR session.");
} else {
_sv_ev_incoming_pgp(chatwin, new_win, message, TRUE);
}
} else {
// otr or plain
_sv_ev_incoming_otr(chatwin, new_win, message);
}
rosterwin_roster();
return;
}
void
sv_ev_incoming_carbon(ProfMessage* message)
{
gboolean new_win = FALSE;
ProfChatWin* chatwin = wins_get_chat(message->from_jid->barejid);
if (!chatwin) {
chatwin = chatwin_new(message->from_jid->barejid);
new_win = TRUE;
#ifdef HAVE_OMEMO
if (omemo_automatic_start(message->from_jid->barejid)) {
omemo_start_session(message->from_jid->barejid);
chatwin->is_omemo = TRUE;
}
#endif
}
gboolean logit = TRUE;
if (message->type == PROF_MSG_TYPE_MUCPM) {
logit = FALSE;
}
if (message->enc == PROF_MSG_ENC_OX) {
_sv_ev_incoming_ox(chatwin, new_win, message, logit);
} else if (message->encrypted) {
_sv_ev_incoming_pgp(chatwin, new_win, message, logit);
} else if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message, logit);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message, logit);
}
rosterwin_roster();
return;
}
void
sv_ev_message_receipt(const char* const barejid, const char* const id)
{
ProfChatWin* chatwin = wins_get_chat(barejid);
if (!chatwin)
return;
chatwin_receipt_received(chatwin, id);
}
void
sv_ev_typing(char* barejid, char* resource)
{
ui_contact_typing(barejid, resource);
if (wins_chat_exists(barejid)) {
chat_session_recipient_typing(barejid, resource);
}
}
void
sv_ev_paused(char* barejid, char* resource)
{
if (wins_chat_exists(barejid)) {
chat_session_recipient_paused(barejid, resource);
}
}
void
sv_ev_inactive(char* barejid, char* resource)
{
if (wins_chat_exists(barejid)) {
chat_session_recipient_inactive(barejid, resource);
}
}
void
sv_ev_gone(const char* const barejid, const char* const resource)
{
if (barejid && resource) {
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;
}
if (show_message) {
chatwin_recipient_gone(chatwin);
}
}
}
if (wins_chat_exists(barejid)) {
chat_session_recipient_gone(barejid, resource);
}
}
void
sv_ev_activity(const char* const barejid, const char* const resource, gboolean send_states)
{
if (wins_chat_exists(barejid)) {
chat_session_recipient_active(barejid, resource, send_states);
}
}
void
sv_ev_subscription(const char* barejid, jabber_subscr_t type)
{
switch (type) {
case PRESENCE_SUBSCRIBE:
/* TODO: auto-subscribe if needed */
cons_show("Received authorization request from %s", barejid);
log_info("Received authorization request from %s", barejid);
ui_print_system_msg_from_recipient(barejid, "Authorization request, type '/sub allow' to accept or '/sub deny' to reject");
if (prefs_get_boolean(PREF_NOTIFY_SUB)) {
notify_subscription(barejid);
}
break;
case PRESENCE_SUBSCRIBED:
log_info("Subscription received from %s", barejid);
cons_show("Subscription received from %s", barejid);
PContact contact = roster_get_contact(barejid);
if (contact == NULL) {
ui_print_system_msg_from_recipient(barejid, "Subscribed");
}
break;
case PRESENCE_UNSUBSCRIBED:
cons_show("%s deleted subscription", barejid);
log_info("%s deleted subscription", barejid);
ui_print_system_msg_from_recipient(barejid, "Unsubscribed");
break;
default:
/* unknown type */
break;
}
}
void
sv_ev_contact_offline(char* barejid, char* resource, char* status)
{
gboolean updated = roster_contact_offline(barejid, resource, status);
if (resource && updated) {
plugins_on_contact_offline(barejid, resource, status);
ui_contact_offline(barejid, resource, status);
}
#ifdef HAVE_LIBOTR
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin && otr_is_secure(barejid)) {
chatwin_otr_unsecured(chatwin);
otr_end_session(chatwin->barejid);
}
#endif
rosterwin_roster();
chat_session_remove(barejid);
}
void
sv_ev_contact_online(char* barejid, Resource* resource, GDateTime* last_activity, char* pgpsig)
{
gboolean updated = roster_update_presence(barejid, resource, last_activity);
if (updated) {
plugins_on_contact_presence(barejid, resource->name, string_from_resource_presence(resource->presence),
resource->status, resource->priority);
ui_contact_online(barejid, resource, last_activity);
}
#ifdef HAVE_LIBGPGME
if (pgpsig) {
p_gpg_verify(barejid, pgpsig);
}
#endif
rosterwin_roster();
chat_session_remove(barejid);
}
void
sv_ev_leave_room(const char* const room)
{
muc_leave(room);
ui_leave_room(room);
}
void
sv_ev_room_destroy(const char* const room)
{
muc_leave(room);
ui_room_destroy(room);
}
void
sv_ev_room_destroyed(const char* const room, const char* const new_jid, const char* const password,
const char* const reason)
{
muc_leave(room);
ui_room_destroyed(room, reason, new_jid, password);
}
void
sv_ev_room_kicked(const char* const room, const char* const actor, const char* const reason)
{
muc_leave(room);
ui_room_kicked(room, actor, reason);
}
void
sv_ev_room_banned(const char* const room, const char* const actor, const char* const reason)
{
muc_leave(room);
ui_room_banned(room, actor, reason);
}
void
sv_ev_room_occupant_offline(const char* const room, const char* const nick,
const char* const show, const char* const status)
{
muc_roster_remove(room, nick);
char* muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && (g_strcmp0(muc_status_pref, "none") != 0)) {
mucwin_occupant_offline(mucwin, nick);
}
g_free(muc_status_pref);
Jid* jidp = jid_create_from_bare_and_resource(room, nick);
ProfPrivateWin* privwin = wins_get_private(jidp->fulljid);
jid_destroy(jidp);
if (privwin != NULL) {
privwin_occupant_offline(privwin);
}
occupantswin_occupants(room);
rosterwin_roster();
}
void
sv_ev_room_occupent_kicked(const char* const room, const char* const nick, const char* const actor,
const char* const reason)
{
muc_roster_remove(room, nick);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin) {
mucwin_occupant_kicked(mucwin, nick, actor, reason);
}
Jid* jidp = jid_create_from_bare_and_resource(room, nick);
ProfPrivateWin* privwin = wins_get_private(jidp->fulljid);
jid_destroy(jidp);
if (privwin != NULL) {
privwin_occupant_kicked(privwin, actor, reason);
}
occupantswin_occupants(room);
rosterwin_roster();
}
void
sv_ev_room_occupent_banned(const char* const room, const char* const nick, const char* const actor,
const char* const reason)
{
muc_roster_remove(room, nick);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin) {
mucwin_occupant_banned(mucwin, nick, actor, reason);
}
Jid* jidp = jid_create_from_bare_and_resource(room, nick);
muc_members_remove(room, jidp->fulljid);
ProfPrivateWin* privwin = wins_get_private(jidp->fulljid);
jid_destroy(jidp);
if (privwin != NULL) {
privwin_occupant_banned(privwin, actor, reason);
}
occupantswin_occupants(room);
rosterwin_roster();
}
void
sv_ev_roster_update(const char* const barejid, const char* const name,
GSList* groups, const char* const subscription, gboolean pending_out)
{
roster_update(barejid, name, groups, subscription, pending_out);
rosterwin_roster();
}
void
sv_ev_xmpp_stanza(const char* const msg)
{
ProfXMLWin* xmlwin = wins_get_xmlconsole();
if (xmlwin) {
xmlwin_show(xmlwin, msg);
}
}
void
sv_ev_muc_self_online(const char* const room, const char* const nick, gboolean config_required,
const char* const role, const char* const affiliation, const char* const actor, const char* const reason,
const char* const jid, const char* const show, const char* const status)
{
muc_roster_add(room, nick, jid, role, affiliation, show, status);
char* old_role = muc_role_str(room);
char* old_affiliation = muc_affiliation_str(room);
muc_set_role(room, role);
muc_set_affiliation(room, affiliation);
// handle self nick change
if (muc_nick_change_pending(room)) {
muc_nick_change_complete(room, nick);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin) {
mucwin_nick_change(mucwin, nick);
}
// handle roster complete
} else if (!muc_roster_complete(room)) {
if (muc_autojoin(room)) {
ui_room_join(room, FALSE);
} else {
ui_room_join(room, TRUE);
}
Jid* jidp = jid_create(room);
if (jidp->domainpart) {
muc_confserver_add(jidp->domainpart);
}
jid_destroy(jidp);
iq_room_info_request(room, FALSE);
if (muc_invites_contain(room)) {
if (prefs_get_boolean(PREF_BOOKMARK_INVITE) && !bookmark_exists(room)) {
bookmark_add(room, nick, muc_invite_password(room), "on", NULL);
}
muc_invites_remove(room);
}
muc_roster_set_complete(room);
// show roster if occupants list disabled by default
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && !prefs_get_boolean(PREF_OCCUPANTS)) {
GList* occupants = muc_roster(room);
mucwin_roster(mucwin, occupants, NULL);
g_list_free(occupants);
}
char* subject = muc_subject(room);
if (mucwin && subject) {
mucwin_subject(mucwin, NULL, subject);
}
GList* pending_broadcasts = muc_pending_broadcasts(room);
if (mucwin && pending_broadcasts) {
GList* curr = pending_broadcasts;
while (curr) {
mucwin_broadcast(mucwin, curr->data);
curr = g_list_next(curr);
}
}
// room configuration required
if (config_required) {
muc_set_requires_config(room, TRUE);
if (mucwin) {
mucwin_requires_config(mucwin);
}
}
rosterwin_roster();
// check for change in role/affiliation
} else {
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
// both changed
if ((g_strcmp0(role, old_role) != 0) && (g_strcmp0(affiliation, old_affiliation) != 0)) {
mucwin_role_and_affiliation_change(mucwin, role, affiliation, actor, reason);
// role changed
} else if (g_strcmp0(role, old_role) != 0) {
mucwin_role_change(mucwin, role, actor, reason);
// affiliation changed
} else if (g_strcmp0(affiliation, old_affiliation) != 0) {
mucwin_affiliation_change(mucwin, affiliation, actor, reason);
}
}
}
occupantswin_occupants(room);
}
void
sv_ev_muc_occupant_online(const char* const room, const char* const nick, const char* const jid,
const char* const role, const char* const affiliation, const char* const actor, const char* const reason,
const char* const show, const char* const status)
{
Occupant* occupant = muc_roster_item(room, nick);
const char* old_role = NULL;
const char* old_affiliation = NULL;
if (occupant) {
old_role = muc_occupant_role_str(occupant);
old_affiliation = muc_occupant_affiliation_str(occupant);
}
gboolean updated = muc_roster_add(room, nick, jid, role, affiliation, show, status);
if (jid != NULL && affiliation != NULL) {
muc_members_update(room, jid, affiliation);
}
// not yet finished joining room
if (!muc_roster_complete(room)) {
return;
}
// handle nickname change
char* old_nick = muc_roster_nick_change_complete(room, nick);
if (old_nick) {
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin) {
mucwin_occupant_nick_change(mucwin, old_nick, nick);
wins_private_nick_change(mucwin->roomjid, old_nick, nick);
}
free(old_nick);
occupantswin_occupants(room);
rosterwin_roster();
return;
}
// joined room
if (!occupant) {
char* muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && g_strcmp0(muc_status_pref, "none") != 0) {
mucwin_occupant_online(mucwin, nick, role, affiliation, show, status);
}
g_free(muc_status_pref);
if (mucwin) {
Jid* jidp = jid_create_from_bare_and_resource(mucwin->roomjid, nick);
ProfPrivateWin* privwin = wins_get_private(jidp->fulljid);
jid_destroy(jidp);
if (privwin) {
privwin_occupant_online(privwin);
}
}
occupantswin_occupants(room);
rosterwin_roster();
return;
}
// presence updated
if (updated) {
char* muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && (g_strcmp0(muc_status_pref, "all") == 0)) {
mucwin_occupant_presence(mucwin, nick, show, status);
}
g_free(muc_status_pref);
occupantswin_occupants(room);
// presence unchanged, check for role/affiliation change
} else {
ProfMucWin* mucwin = wins_get_muc(room);
if (mucwin && prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
// both changed
if ((g_strcmp0(role, old_role) != 0) && (g_strcmp0(affiliation, old_affiliation) != 0)) {
mucwin_occupant_role_and_affiliation_change(mucwin, nick, role, affiliation, actor, reason);
// role changed
} else if (g_strcmp0(role, old_role) != 0) {
mucwin_occupant_role_change(mucwin, nick, role, actor, reason);
// affiliation changed
} else if (g_strcmp0(affiliation, old_affiliation) != 0) {
mucwin_occupant_affiliation_change(mucwin, nick, affiliation, actor, reason);
}
}
occupantswin_occupants(room);
}
rosterwin_roster();
}
int
sv_ev_certfail(const char* const errormsg, const TLSCertificate* cert)
{
// check profanity trusted certs
if (tlscerts_exists(cert->fingerprint)) {
cafile_add(cert);
return 1;
}
// check current cert
char* current_fp = tlscerts_get_current();
if (current_fp && g_strcmp0(current_fp, cert->fingerprint) == 0) {
return 1;
}
cons_show("");
cons_show_error("TLS certificate verification failed: %s", errormsg);
cons_show_tlscert(cert);
cons_show("");
cons_show("Use '/tls allow' to accept this certificate.");
cons_show("Use '/tls always' to accept this certificate permanently.");
cons_show("Use '/tls deny' to reject this certificate.");
cons_show("");
ui_update();
char* cmd = ui_get_line();
while ((g_strcmp0(cmd, "/tls allow") != 0)
&& (g_strcmp0(cmd, "/tls always") != 0)
&& (g_strcmp0(cmd, "/tls deny") != 0)
&& (g_strcmp0(cmd, "/quit") != 0)) {
cons_show("Use '/tls allow' to accept this certificate.");
cons_show("Use '/tls always' to accept this certificate permanently.");
cons_show("Use '/tls deny' to reject this certificate.");
cons_show("");
ui_update();
free(cmd);
cmd = ui_get_line();
}
if (g_strcmp0(cmd, "/tls allow") == 0) {
cons_show("Continuing with connection.");
tlscerts_set_current(cert->fingerprint);
free(cmd);
return 1;
} else if (g_strcmp0(cmd, "/tls always") == 0) {
cons_show("Adding %s to trusted certificates.", cert->fingerprint);
if (!tlscerts_exists(cert->fingerprint)) {
tlscerts_add(cert);
cafile_add(cert);
}
free(cmd);
return 1;
} else if (g_strcmp0(cmd, "/quit") == 0) {
prof_set_quit();
free(cmd);
return 0;
} else {
cons_show("Aborting connection.");
free(cmd);
return 0;
}
}
void
sv_ev_lastactivity_response(const char* const from, const int seconds, const char* const msg)
{
Jid* jidp = jid_create(from);
if (!jidp) {
return;
}
GDateTime* now = g_date_time_new_now_local();
GDateTime* active = g_date_time_add_seconds(now, 0 - seconds);
gchar* date_fmt = NULL;
char* time_pref = prefs_get_string(PREF_TIME_LASTACTIVITY);
date_fmt = g_date_time_format(active, time_pref);
g_free(time_pref);
assert(date_fmt != NULL);
// full jid - last activity
if (jidp->resourcepart) {
if (seconds == 0) {
if (msg) {
cons_show("%s currently active, status: %s", from, msg);
} else {
cons_show("%s currently active", from);
}
} else {
if (msg) {
cons_show("%s last active %s, status: %s", from, date_fmt, msg);
} else {
cons_show("%s last active %s", from, date_fmt);
}
}
// barejid - last logged in
} else if (jidp->localpart) {
if (seconds == 0) {
if (msg) {
cons_show("%s currently logged in, status: %s", from, msg);
} else {
cons_show("%s currently logged in", from);
}
} else {
if (msg) {
cons_show("%s last logged in %s, status: %s", from, date_fmt, msg);
} else {
cons_show("%s last logged in %s", from, date_fmt);
}
}
// domain only - uptime
} else {
int left = seconds;
int days = seconds / 86400;
left = left - days * 86400;
int hours = left / 3600;
left = left - hours * 3600;
int minutes = left / 60;
left = left - minutes * 60;
int seconds = left;
cons_show("%s up since %s, uptime %d days, %d hrs, %d mins, %d secs", from, date_fmt, days, hours, minutes, seconds);
}
g_date_time_unref(now);
g_date_time_unref(active);
g_free(date_fmt);
jid_destroy(jidp);
}
void
sv_ev_bookmark_autojoin(Bookmark* bookmark)
{
if (bookmark_ignored(bookmark)) {
return;
}
char* nick = NULL;
if (bookmark->nick) {
nick = strdup(bookmark->nick);
} else {
char* account_name = session_get_account_name();
ProfAccount* account = accounts_get_account(account_name);
nick = strdup(account->muc_nick);
account_free(account);
}
log_debug("Autojoin %s with nick=%s", bookmark->barejid, nick);
if (!muc_active(bookmark->barejid)) {
muc_join(bookmark->barejid, nick, bookmark->password, TRUE);
presence_join_room(bookmark->barejid, nick, bookmark->password);
iq_room_affiliation_list(bookmark->barejid, "member", false);
iq_room_affiliation_list(bookmark->barejid, "admin", false);
iq_room_affiliation_list(bookmark->barejid, "owner", false);
}
free(nick);
}
static void
_cut(ProfMessage* message, const char* cut)
{
if (strstr(message->plain, cut)) {
char** split = g_strsplit(message->plain, cut, -1);
free(message->plain);
message->plain = g_strjoinv("", split);
g_strfreev(split);
}
}
static void
_clean_incoming_message(ProfMessage* message)
{
_cut(message, "\u200E");
_cut(message, "\u200F");
}