Files
cproof/src/event/server_events.c
Michael Vetter 53c3115976 Don't print subscribed message if contact is already in roster
This occured with a user running Cisco Jabber. It seems this server
sends repeated presence subscribed stanzas. And although I find this
strange according to RFC this seems to be ok.

So let's filter them and only display in the console output and to the
log. But don't open seperate windows.

Fix https://github.com/profanity-im/profanity/issues/1165
2019-08-01 20:48:23 +02:00

1390 lines
40 KiB
C

/*
* server_events.c
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 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 "profanity.h"
#include "log.h"
#include "config/preferences.h"
#include "config/tlscerts.h"
#include "config/account.h"
#include "config/scripts.h"
#include "event/client_events.h"
#include "event/common.h"
#include "plugins/plugins.h"
#include "ui/window_list.h"
#include "xmpp/muc.h"
#include "xmpp/chat_session.h"
#include "xmpp/roster_list.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"
void
sv_ev_login_account_success(char *account_name, gboolean secured)
{
ProfAccount *account = accounts_get_account(account_name);
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
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)
{
if (prefs_get_boolean(PREF_ROSTER)) {
ui_show_roster();
}
roster_process_pending_presence();
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
ProfWin *win = wins_get_current();
char *theme = prefs_get_string(PREF_THEME);
win_clear(win);
theme_init(theme);
prefs_free_string(theme);
ui_init();
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);
if (last_activity_str) {
GDateTime *nowdt = g_date_time_new_now_utc();
GTimeVal lasttv;
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);
int diff_secs = (diff_micros / 1000) / 1000;
if (prefs_get_boolean(PREF_LASTACTIVITY)) {
connection_set_presence_msg(NULL);
cl_ev_presence_send(conn_presence, diff_secs);
} else {
connection_set_presence_msg(NULL);
cl_ev_presence_send(conn_presence, 0);
}
g_date_time_unref(lastdt);
} else {
connection_set_presence_msg(NULL);
cl_ev_presence_send(conn_presence, 0);
}
free(last_activity_str);
g_date_time_unref(nowdt);
} else {
connection_set_presence_msg(NULL);
cl_ev_presence_send(conn_presence, 0);
}
const char *fulljid = connection_get_fulljid();
plugins_on_connect(account_name, fulljid);
#ifdef HAVE_OMEMO
omemo_start_sessions();
#endif
}
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)
{
ProfMucWin *mucwin = wins_get_muc(message->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->jid->resourcepart, message->timestamp, message->plain);
}
}
}
void
sv_ev_room_message(ProfMessage *message)
{
ProfMucWin *mucwin = wins_get_muc(message->jid->barejid);
if (!mucwin) {
return;
}
char *mynick = muc_nick(mucwin->roomjid);
if (message->enc == PROF_MSG_ENC_OMEMO) {
groupchat_log_omemo_msg_in(message->jid->barejid, message->jid->resourcepart, message->plain);
} else {
groupchat_log_msg_in(message->jid->barejid, message->jid->resourcepart, message->plain);
}
char *old_plain = message->plain;
message->plain = plugins_pre_room_message_display(message->jid->barejid, message->jid->resourcepart, message->plain);
gboolean whole_word = prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD);
gboolean case_sensitive = prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE);
char *message_search = case_sensitive ? strdup(message->plain) : g_utf8_strdown(message->plain, -1);
char *mynick_search = case_sensitive ? strdup(mynick) : g_utf8_strdown(mynick, -1);
GSList *mentions = NULL;
mentions = prof_occurrences(mynick_search, message_search, 0, whole_word, &mentions);
gboolean mention = g_slist_length(mentions) > 0;
g_free(message_search);
g_free(mynick_search);
GList *triggers = prefs_message_get_triggers(message->plain);
mucwin_incoming_msg(mucwin, message, mentions, triggers);
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->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->jid->resourcepart) != 0) && (prefs_get_boolean(PREF_FLASH))) {
flash();
}
cons_show_incoming_room_message(message->jid->resourcepart, mucwin->roomjid, num, mention, triggers, mucwin->unread);
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->jid->resourcepart, message->plain, mention, triggers != NULL)) {
Jid *jidp = jid_create(mucwin->roomjid);
notify_room_message(message->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->jid->barejid, message->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->jid->fulljid, message->plain);
ProfPrivateWin *privatewin = wins_get_private(message->jid->fulljid);
if (privatewin == NULL) {
ProfWin *window = wins_new_private(message->jid->fulljid);
privatewin = (ProfPrivateWin*)window;
}
privwin_incoming_msg(privatewin, message);
plugins_post_priv_message_display(message->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->jid->fulljid, message->plain);
ProfPrivateWin *privatewin = wins_get_private(message->jid->fulljid);
if (privatewin == NULL) {
ProfWin *window = wins_new_private(message->jid->fulljid);
privatewin = (ProfPrivateWin*)window;
}
privwin_incoming_msg(privatewin, message);
plugins_post_priv_message_display(message->jid->fulljid, message->plain);
free(message->plain);
message->plain = old_plain;
}
void
sv_ev_outgoing_carbon(ProfMessage *message)
{
ProfChatWin *chatwin = wins_get_chat(message->jid->barejid);
if (!chatwin) {
chatwin = chatwin_new(message->jid->barejid);
}
chat_state_active(chatwin->state);
#ifdef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
if (message->encrypted) {
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_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
} else {
message->enc = PROF_MSG_ENC_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
return;
#endif
#endif
#ifndef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->enc == PROF_MSG_ENC_OMEMO) {
chatwin_outgoing_carbon(chatwin, message);
} else {
message->enc = PROF_MSG_ENC_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
return;
#endif
#endif
#ifdef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->enc == PROF_MSG_ENC_OMEMO) {
chatwin_outgoing_carbon(chatwin, message);
} else if (message->encrypted) {
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_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
} else {
message->enc = PROF_MSG_ENC_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
return;
#endif
#endif
#ifndef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
if (message->body) {
message->enc = PROF_MSG_ENC_PLAIN;
message->plain = strdup(message->body);
chatwin_outgoing_carbon(chatwin, message);
}
#endif
#endif
}
#ifdef HAVE_LIBGPGME
static void
_sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message)
{
message->plain = p_gpg_decrypt(message->encrypted);
if (message->plain) {
message->enc = PROF_MSG_ENC_PGP;
chatwin_incoming_msg(chatwin, message, new_win);
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_PLAIN;
message->plain = strdup(message->body);
chatwin_incoming_msg(chatwin, message, new_win);
chat_log_msg_in(message);
chatwin->pgp_recv = FALSE;
}
}
#endif
#ifdef HAVE_LIBOTR
static void
_sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message)
{
gboolean decrypted = FALSE;
message->plain = otr_on_message_recv(message->jid->barejid, message->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_PLAIN;
}
chatwin_incoming_msg(chatwin, message, new_win);
chat_log_otr_msg_in(message);
otr_free_message(message->plain);
message->plain = NULL;
chatwin->pgp_recv = FALSE;
}
}
#endif
#ifdef HAVE_OMEMO
static void
_sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message)
{
chatwin_incoming_msg(chatwin, message, new_win);
chat_log_omemo_msg_in(message);
chatwin->pgp_recv = FALSE;
}
#endif
static void
_sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message)
{
if (message->body) {
message->enc = PROF_MSG_ENC_PLAIN;
message->plain = strdup(message->body);
chatwin_incoming_msg(chatwin, message, new_win);
chat_log_msg_in(message);
chatwin->pgp_recv = FALSE;
}
}
void
sv_ev_incoming_message(ProfMessage *message)
{
gboolean new_win = FALSE;
ProfChatWin *chatwin = wins_get_chat(message->jid->barejid);
if (!chatwin) {
ProfWin *window = wins_new_chat(message->jid->barejid);
chatwin = (ProfChatWin*)window;
new_win = TRUE;
#ifdef HAVE_OMEMO
if (omemo_automatic_start(message->jid->barejid)) {
omemo_start_session(message->jid->barejid);
chatwin->is_omemo = TRUE;
}
#endif
}
// OTR suported, PGP supported, OMEMO unsupported
#ifdef HAVE_LIBOTR
#ifdef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
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);
}
} else {
_sv_ev_incoming_otr(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR supported, PGP unsupported, OMEMO unsupported
#ifdef HAVE_LIBOTR
#ifndef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
_sv_ev_incoming_otr(chatwin, new_win, message);
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR unsupported, PGP supported, OMEMO unsupported
#ifndef HAVE_LIBOTR
#ifdef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
if (message->encrypted) {
_sv_ev_incoming_pgp(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR suported, PGP supported, OMEMO supported
#ifdef HAVE_LIBOTR
#ifdef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
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);
}
} else if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_otr(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR supported, PGP unsupported, OMEMO supported
#ifdef HAVE_LIBOTR
#ifndef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_otr(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR unsupported, PGP supported, OMEMO supported
#ifndef HAVE_LIBOTR
#ifdef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->encrypted) {
_sv_ev_incoming_pgp(chatwin, new_win, message);
} else if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR unsupported, PGP unsupported, OMEMO supported
#ifndef HAVE_LIBOTR
#ifndef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#endif
// OTR unsupported, PGP unsupported, OMEMO unsupported
#ifndef HAVE_LIBOTR
#ifndef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
_sv_ev_incoming_plain(chatwin, new_win, message);
rosterwin_roster();
return;
#endif
#endif
#endif
}
void
sv_ev_incoming_carbon(ProfMessage *message)
{
gboolean new_win = FALSE;
ProfChatWin *chatwin = wins_get_chat(message->jid->barejid);
if (!chatwin) {
ProfWin *window = wins_new_chat(message->jid->barejid);
chatwin = (ProfChatWin*)window;
new_win = TRUE;
#ifdef HAVE_OMEMO
if (omemo_automatic_start(message->jid->barejid)) {
omemo_start_session(message->jid->barejid);
chatwin->is_omemo = TRUE;
}
#endif
}
#ifdef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
if (message->encrypted) {
_sv_ev_incoming_pgp(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#ifdef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->encrypted) {
_sv_ev_incoming_pgp(chatwin, new_win, message);
} else if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#ifndef HAVE_LIBGPGME
#ifdef HAVE_OMEMO
if (message->enc == PROF_MSG_ENC_OMEMO) {
_sv_ev_incoming_omemo(chatwin, new_win, message);
} else {
_sv_ev_incoming_plain(chatwin, new_win, message);
}
rosterwin_roster();
return;
#endif
#endif
#ifndef HAVE_LIBGPGME
#ifndef HAVE_OMEMO
_sv_ev_incoming_plain(chatwin, new_win, message);
rosterwin_roster();
return;
#endif
#endif
}
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);
}
prefs_free_string(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);
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");
}
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);
// 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);
}
prefs_free_string(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);
}
prefs_free_string(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, TLSCertificate *cert)
{
// check profanity trusted certs
if (tlscerts_exists(cert->fingerprint)) {
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);
}
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);
prefs_free_string(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)
{
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)) {
presence_join_room(bookmark->barejid, nick, bookmark->password);
muc_join(bookmark->barejid, nick, bookmark->password, TRUE);
}
free(nick);
}