/* * presence.c * * Copyright (C) 2012 - 2018 James Booth * * 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 . * * 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 #include #include #include #include #ifdef HAVE_LIBMESODE #include #endif #ifdef HAVE_LIBSTROPHE #include #endif #include "profanity.h" #include "log.h" #include "common.h" #include "config/preferences.h" #include "event/server_events.h" #include "plugins/plugins.h" #include "ui/ui.h" #include "xmpp/connection.h" #include "xmpp/capabilities.h" #include "xmpp/session.h" #include "xmpp/stanza.h" #include "xmpp/iq.h" #include "xmpp/xmpp.h" #include "xmpp/muc.h" static Autocomplete sub_requests_ac; static int _presence_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata); static void _presence_error_handler(xmpp_stanza_t *const stanza); static void _unavailable_handler(xmpp_stanza_t *const stanza); static void _subscribe_handler(xmpp_stanza_t *const stanza); static void _subscribed_handler(xmpp_stanza_t *const stanza); static void _unsubscribed_handler(xmpp_stanza_t *const stanza); static void _muc_user_handler(xmpp_stanza_t *const stanza); static void _available_handler(xmpp_stanza_t *const stanza); void _send_caps_request(char *node, char *caps_key, char *id, char *from); static void _send_room_presence(xmpp_stanza_t *presence); static void _send_presence_stanza(xmpp_stanza_t *const stanza); void presence_sub_requests_init(void) { sub_requests_ac = autocomplete_new(); } void presence_handlers_init(void) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_handler_add(conn, _presence_handler, NULL, STANZA_NAME_PRESENCE, NULL, ctx); } void presence_subscription(const char *const jid, const jabber_subscr_t action) { assert(jid != NULL); Jid *jidp = jid_create(jid); autocomplete_remove(sub_requests_ac, jidp->barejid); const char *type = NULL; switch (action) { case PRESENCE_SUBSCRIBE: log_debug("Sending presence subscribe: %s", jid); type = STANZA_TYPE_SUBSCRIBE; break; case PRESENCE_SUBSCRIBED: log_debug("Sending presence subscribed: %s", jid); type = STANZA_TYPE_SUBSCRIBED; break; case PRESENCE_UNSUBSCRIBED: log_debug("Sending presence usubscribed: %s", jid); type = STANZA_TYPE_UNSUBSCRIBED; break; default: break; } if (!type) { log_error("Attempt to send unknown subscription action: %s", jid); return; } xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *presence = xmpp_presence_new(ctx); char *id = create_unique_id("sub"); xmpp_stanza_set_id(presence, id); free(id); xmpp_stanza_set_type(presence, type); xmpp_stanza_set_to(presence, jidp->barejid); jid_destroy(jidp); _send_presence_stanza(presence); xmpp_stanza_release(presence); } GList* presence_get_subscription_requests(void) { return autocomplete_create_list(sub_requests_ac); } gint presence_sub_request_count(void) { return autocomplete_length(sub_requests_ac); } void presence_clear_sub_requests(void) { autocomplete_clear(sub_requests_ac); } char* presence_sub_request_find(const char *const search_str, gboolean previous) { return autocomplete_complete(sub_requests_ac, search_str, TRUE, previous); } gboolean presence_sub_request_exists(const char *const bare_jid) { gboolean result = FALSE; GList *requests = autocomplete_create_list(sub_requests_ac); GList *curr = requests; while (curr) { if (strcmp(curr->data, bare_jid) == 0) { result = TRUE; break; } curr = g_list_next(curr); } g_list_free_full(requests, free); return result; } void presence_reset_sub_request_search(void) { autocomplete_reset(sub_requests_ac); } void presence_send(const resource_presence_t presence_type, const int idle, char *signed_status) { if (connection_get_status() != JABBER_CONNECTED) { log_warning("Error setting presence, not connected."); return; } char *msg = connection_get_presence_msg(); if (msg) { log_debug("Updating presence: %s, \"%s\"", string_from_resource_presence(presence_type), msg); } else { log_debug("Updating presence: %s", string_from_resource_presence(presence_type)); } const int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); connection_set_priority(pri); xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *presence = xmpp_presence_new(ctx); char *id = create_unique_id("presence"); xmpp_stanza_set_id(presence, id); free(id); const char *show = stanza_get_presence_string_from_type(presence_type); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, msg); if (signed_status) { xmpp_stanza_t *x = xmpp_stanza_new(ctx); xmpp_stanza_set_name(x, STANZA_NAME_X); xmpp_stanza_set_ns(x, STANZA_NS_SIGNED); xmpp_stanza_t *signed_text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(signed_text, signed_status); xmpp_stanza_add_child(x, signed_text); xmpp_stanza_release(signed_text); xmpp_stanza_add_child(presence, x); xmpp_stanza_release(x); } stanza_attach_priority(ctx, presence, pri); if (idle > 0) { stanza_attach_last_activity(ctx, presence, idle); } stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); _send_room_presence(presence); xmpp_stanza_release(presence); // set last presence for account const char *last = show; if (last == NULL) { last = STANZA_TEXT_ONLINE; } char *account = session_get_account_name(); accounts_set_last_presence(account, last); accounts_set_last_status(account, msg); } static void _send_room_presence(xmpp_stanza_t *presence) { GList *rooms = muc_rooms(); GList *curr = rooms; while (curr) { const char *room = curr->data; const char *nick = muc_nick(room); if (nick) { char *full_room_jid = create_fulljid(room, nick); xmpp_stanza_set_to(presence, full_room_jid); log_debug("Sending presence to room: %s", full_room_jid); free(full_room_jid); _send_presence_stanza(presence); } curr = g_list_next(curr); } g_list_free(rooms); } void presence_join_room(const char *const room, const char *const nick, const char *const passwd) { Jid *jid = jid_create_from_bare_and_resource(room, nick); log_debug("Sending room join presence to: %s", jid->fulljid); resource_presence_t presence_type = accounts_get_last_presence(session_get_account_name()); const char *show = stanza_get_presence_string_from_type(presence_type); char *status = connection_get_presence_msg(); int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *presence = stanza_create_room_join_presence(ctx, jid->fulljid, passwd); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, status); stanza_attach_priority(ctx, presence, pri); stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); xmpp_stanza_release(presence); jid_destroy(jid); } void presence_change_room_nick(const char *const room, const char *const nick) { assert(room != NULL); assert(nick != NULL); log_debug("Sending room nickname change to: %s, nick: %s", room, nick); resource_presence_t presence_type = accounts_get_last_presence(session_get_account_name()); const char *show = stanza_get_presence_string_from_type(presence_type); char *status = connection_get_presence_msg(); int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); char *full_room_jid = create_fulljid(room, nick); xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *presence = stanza_create_room_newnick_presence(ctx, full_room_jid); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, status); stanza_attach_priority(ctx, presence, pri); stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); xmpp_stanza_release(presence); free(full_room_jid); } void presence_leave_chat_room(const char *const room_jid) { assert(room_jid != NULL); char *nick = muc_nick(room_jid); if (!nick) { log_error("Could not get nickname for room: %s", room_jid); return; } log_debug("Sending room leave presence to: %s", room_jid); xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *presence = stanza_create_room_leave_presence(ctx, room_jid, nick); _send_presence_stanza(presence); xmpp_stanza_release(presence); } static int _presence_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { log_debug("Presence stanza handler fired"); char *text = NULL; size_t text_size; xmpp_stanza_to_text(stanza, &text, &text_size); gboolean cont = plugins_on_presence_stanza_receive(text); xmpp_free(connection_get_ctx(), text); if (!cont) { return 1; } const char *type = xmpp_stanza_get_type(stanza); if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { _presence_error_handler(stanza); } if (g_strcmp0(type, STANZA_TYPE_UNAVAILABLE) == 0) { _unavailable_handler(stanza); } if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBE) == 0) { _subscribe_handler(stanza); } if (g_strcmp0(type, STANZA_TYPE_SUBSCRIBED) == 0) { _subscribed_handler(stanza); } if (g_strcmp0(type, STANZA_TYPE_UNSUBSCRIBED) == 0) { _unsubscribed_handler(stanza); } xmpp_stanza_t *mucuser = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); if (mucuser) { _muc_user_handler(stanza); } _available_handler(stanza); return 1; } static void _presence_error_handler(xmpp_stanza_t *const stanza) { const char *xmlns = NULL; xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X); if (x) { xmlns = xmpp_stanza_get_ns(x); } const char *from = xmpp_stanza_get_from(stanza); xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR); // handle MUC join errors if (g_strcmp0(xmlns, STANZA_NS_MUC) == 0) { const char *error_cond = NULL; xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_ns(error_stanza, STANZA_NS_STANZAS); if (reason_st) { error_cond = xmpp_stanza_get_name(reason_st); } if (error_cond == NULL) { error_cond = "unknown"; } Jid *fulljid = jid_create(from);
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - edit/009-sandbox-test.mu</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="none">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.Conceal { color: #4e4e4e; }
.muScenario { color: #00af00; }
.Special { color: #c00000; }
.Delimiter { color: #800080; }
.SalientComment { color: #00ffff; }
.Constant { color: #00a0a0; }
.LineNr { color: #444444; }
.muRecipe { color: #ff8700; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
.muControl { color: #c0a020; }
.muData { color: #ffff00; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') ==