|
|
|
@ -8,6 +8,8 @@
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
#include <fmt/core.h>
|
|
|
|
|
#include <fmt/chrono.h>
|
|
|
|
|
#include <strophe.h>
|
|
|
|
|
#include <weechat/weechat-plugin.h>
|
|
|
|
|
|
|
|
|
@ -85,43 +87,23 @@ int connection__version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *
|
|
|
|
|
|
|
|
|
|
int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
|
|
|
|
{
|
|
|
|
|
struct t_account *account = (struct t_account *)userdata;
|
|
|
|
|
struct t_account *account = reinterpret_cast<struct t_account *>(userdata);
|
|
|
|
|
struct t_user *user;
|
|
|
|
|
struct t_channel *channel;
|
|
|
|
|
xmpp_stanza_t *pres__x_signed, *pres__x_muc_user, *show, *idle, *pres__x__item, *pres__c, *pres__status;
|
|
|
|
|
const char *to, *from, *from_bare, *from_res, *type, *role = NULL, *affiliation = NULL, *jid = NULL;
|
|
|
|
|
const char *show__text = NULL, *idle__since = NULL, *certificate = NULL, *node = NULL, *ver = NULL;
|
|
|
|
|
char *clientid = NULL, *status;
|
|
|
|
|
|
|
|
|
|
auto binding = xml::presence(account->context, stanza);
|
|
|
|
|
to = xmpp_stanza_get_to(stanza);
|
|
|
|
|
from = xmpp_stanza_get_from(stanza);
|
|
|
|
|
if (from == NULL)
|
|
|
|
|
if (!binding.from)
|
|
|
|
|
return 1;
|
|
|
|
|
from_bare = xmpp_jid_bare(account->context, from);
|
|
|
|
|
from_res = xmpp_jid_resource(account->context, from);
|
|
|
|
|
type = xmpp_stanza_get_type(stanza);
|
|
|
|
|
show = xmpp_stanza_get_child_by_name(stanza, "show");
|
|
|
|
|
show__text = show ? xmpp_stanza_get_text(show) : NULL;
|
|
|
|
|
idle = xmpp_stanza_get_child_by_name_and_ns(stanza, "idle", "urn:xmpp:idle:1");
|
|
|
|
|
idle__since = idle ? xmpp_stanza_get_attribute(idle, "since") : NULL;
|
|
|
|
|
pres__x_signed = xmpp_stanza_get_child_by_name_and_ns(
|
|
|
|
|
stanza, "x", "jabber:x:signed");
|
|
|
|
|
if (pres__x_signed)
|
|
|
|
|
{
|
|
|
|
|
certificate = xmpp_stanza_get_text(pres__x_signed);
|
|
|
|
|
}
|
|
|
|
|
pres__c = xmpp_stanza_get_child_by_name_and_ns(
|
|
|
|
|
stanza, "c", "http://jabber.org/protocol/caps");
|
|
|
|
|
if (pres__c)
|
|
|
|
|
{
|
|
|
|
|
node = xmpp_stanza_get_attribute(pres__c, "node");
|
|
|
|
|
ver = xmpp_stanza_get_attribute(pres__c, "ver");
|
|
|
|
|
if (node && ver)
|
|
|
|
|
|
|
|
|
|
std::string clientid;
|
|
|
|
|
if (auto caps = binding.capabilities())
|
|
|
|
|
{
|
|
|
|
|
int len = strlen(node)+1+strlen(ver) + 1;
|
|
|
|
|
clientid = (char*)malloc(sizeof(char)*len);
|
|
|
|
|
snprintf(clientid, len, "%s#%s", node, ver);
|
|
|
|
|
auto node = caps->node;
|
|
|
|
|
auto ver = caps->verification;
|
|
|
|
|
|
|
|
|
|
clientid = fmt::format("{}#{}", node, ver);
|
|
|
|
|
|
|
|
|
|
std::string to = binding.to ? binding.to->full : "";
|
|
|
|
|
|
|
|
|
|
xmpp_stanza_t *children[2] = {NULL};
|
|
|
|
|
children[0] = stanza__iq_pubsub_items(account->context, NULL,
|
|
|
|
@ -129,35 +111,24 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void
|
|
|
|
|
children[0] = stanza__iq_pubsub(account->context, NULL,
|
|
|
|
|
children, with_noop("http://jabber.org/protocol/pubsub"));
|
|
|
|
|
children[0] = stanza__iq(account->context, NULL, children, NULL,
|
|
|
|
|
strdup("fetch2"), to ? strdup(to) : NULL, strdup(from_bare),
|
|
|
|
|
strdup("get"));
|
|
|
|
|
strdup("fetch2"), to.size() ? strdup(to.data()) : NULL,
|
|
|
|
|
binding.from->bare.size() ? strdup(binding.from->bare.data()) : NULL, strdup("get"));
|
|
|
|
|
xmpp_send(conn, children[0]);
|
|
|
|
|
xmpp_stanza_release(children[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pres__status = xmpp_stanza_get_child_by_name(stanza, "status");
|
|
|
|
|
status = pres__status ? xmpp_stanza_get_text(pres__status) : NULL;
|
|
|
|
|
pres__x_muc_user = xmpp_stanza_get_child_by_name_and_ns(
|
|
|
|
|
stanza, "x", "http://jabber.org/protocol/muc#user");
|
|
|
|
|
|
|
|
|
|
channel = channel__search(account, from_bare);
|
|
|
|
|
if (weechat_strcasecmp(type, "unavailable") != 0 && !pres__x_muc_user && !channel)
|
|
|
|
|
channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare);
|
|
|
|
|
channel = channel__search(account, std::string(binding.from->bare).data());
|
|
|
|
|
if (!(binding.type && *binding.type == "unavailable") && !binding.muc_user() && !channel)
|
|
|
|
|
channel = channel__new(account, CHANNEL_TYPE_PM, std::string(binding.from->bare).data(), std::string(binding.from->bare).data());
|
|
|
|
|
|
|
|
|
|
if (pres__x_muc_user)
|
|
|
|
|
for (pres__x__item = xmpp_stanza_get_children(pres__x_muc_user);
|
|
|
|
|
pres__x__item; pres__x__item = xmpp_stanza_get_next(pres__x__item))
|
|
|
|
|
if (auto x = binding.muc_user())
|
|
|
|
|
{
|
|
|
|
|
const char *pres__x__item__name = xmpp_stanza_get_name(pres__x__item);
|
|
|
|
|
if (weechat_strcasecmp(pres__x__item__name, "item") != 0)
|
|
|
|
|
for (int& status : x->statuses)
|
|
|
|
|
{
|
|
|
|
|
if (weechat_strcasecmp(pres__x__item__name, "status") == 0)
|
|
|
|
|
{
|
|
|
|
|
const char *pres__x__status__code = xmpp_stanza_get_attribute(
|
|
|
|
|
pres__x__item, "code");
|
|
|
|
|
switch (strtol(pres__x__status__code, NULL, 10))
|
|
|
|
|
switch (status)
|
|
|
|
|
{
|
|
|
|
|
case 100: // Non-Anonymous: [message | Entering a room]: Inform user that any occupant is allowed to see the user's full JID
|
|
|
|
|
if (channel)
|
|
|
|
|
weechat_buffer_set(channel->buffer, "notify", "2");
|
|
|
|
|
break;
|
|
|
|
|
case 101: // : [message (out of band) | Affiliation change]: Inform user that his or her affiliation changed while not in the room
|
|
|
|
@ -200,73 +171,80 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
role = xmpp_stanza_get_attribute(pres__x__item, "role");
|
|
|
|
|
affiliation = xmpp_stanza_get_attribute(pres__x__item, "affiliation");
|
|
|
|
|
jid = xmpp_stanza_get_attribute(pres__x__item, "jid");
|
|
|
|
|
for (auto& item : x->items)
|
|
|
|
|
{
|
|
|
|
|
using xml::xep0045;
|
|
|
|
|
|
|
|
|
|
user = user__search(account, from);
|
|
|
|
|
std::string role(item.role ? xep0045::format_role(*item.role) : "");
|
|
|
|
|
std::string affiliation(item.affiliation ? xep0045::format_affiliation(*item.affiliation) : "");
|
|
|
|
|
std::string jid = item.target ? item.target->full : clientid;
|
|
|
|
|
|
|
|
|
|
user = user__search(account, std::string(binding.from->full).data());
|
|
|
|
|
if (!user)
|
|
|
|
|
user = user__new(account, from,
|
|
|
|
|
channel && weechat_strcasecmp(from_bare, channel->id) == 0
|
|
|
|
|
? from_res : from);
|
|
|
|
|
user->profile.status_text = status ? strdup(status) : NULL;
|
|
|
|
|
user->profile.status = show ? strdup(show__text) : NULL;
|
|
|
|
|
user->profile.idle = idle ? strdup(idle__since) : NULL;
|
|
|
|
|
user->is_away = show ? weechat_strcasecmp(show__text, "away") == 0 : 0;
|
|
|
|
|
user->profile.role = role ? strdup(role) : NULL;
|
|
|
|
|
user->profile.affiliation = affiliation && strcmp(affiliation, "none") != 0
|
|
|
|
|
? strdup(affiliation) : NULL;
|
|
|
|
|
if (certificate && channel)
|
|
|
|
|
{
|
|
|
|
|
user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, certificate);
|
|
|
|
|
user = user__new(account, std::string(binding.from->full).data(),
|
|
|
|
|
channel && std::string(binding.from->bare).data() == channel->id
|
|
|
|
|
? (binding.from->resource.size() ? std::string(binding.from->resource).data() : "")
|
|
|
|
|
: std::string(binding.from->full).data());
|
|
|
|
|
auto status = binding.status();
|
|
|
|
|
auto show = binding.show();
|
|
|
|
|
auto idle = binding.idle_since();
|
|
|
|
|
user->profile.status_text = status ? strdup(status->data()) : NULL;
|
|
|
|
|
user->profile.status = show ? strdup(show->data()) : NULL;
|
|
|
|
|
user->profile.idle = idle ? strdup("2000-01-01T00:00:00.000z") : NULL;
|
|
|
|
|
user->is_away = show ? *show == "away" : false;
|
|
|
|
|
user->profile.role = role.size() ? strdup(role.data()) : NULL;
|
|
|
|
|
user->profile.affiliation = affiliation.size() && affiliation == "none"
|
|
|
|
|
? strdup(affiliation.data()) : NULL;
|
|
|
|
|
if (channel)
|
|
|
|
|
{
|
|
|
|
|
if (auto signature = binding.signature())
|
|
|
|
|
{
|
|
|
|
|
user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, signature->data());
|
|
|
|
|
if (channel->type != CHANNEL_TYPE_MUC)
|
|
|
|
|
channel->pgp.id = user->profile.pgp_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channel)
|
|
|
|
|
{
|
|
|
|
|
if (weechat_strcasecmp(role, "none") == 0)
|
|
|
|
|
channel__remove_member(account, channel, from, status);
|
|
|
|
|
if (weechat_strcasecmp(role.data(), "none") == 0)
|
|
|
|
|
channel__remove_member(account, channel, std::string(binding.from->full).data(), status ? status->data() : nullptr);
|
|
|
|
|
else
|
|
|
|
|
channel__add_member(account, channel, from, jid ? jid : clientid);
|
|
|
|
|
channel__add_member(account, channel, std::string(binding.from->full).data(), jid.data());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
user = user__search(account, from);
|
|
|
|
|
user = user__search(account, std::string(binding.from->full).data());
|
|
|
|
|
if (!user)
|
|
|
|
|
user = user__new(account, from,
|
|
|
|
|
channel && weechat_strcasecmp(from_bare, channel->id) == 0
|
|
|
|
|
? from_res : from);
|
|
|
|
|
user->profile.status_text = status ? strdup(status) : NULL;
|
|
|
|
|
user->profile.status = show ? strdup(show__text) : NULL;
|
|
|
|
|
user->profile.idle = idle ? strdup(idle__since) : NULL;
|
|
|
|
|
user->is_away = show ? weechat_strcasecmp(show__text, "away") == 0 : 0;
|
|
|
|
|
user->profile.role = role ? strdup(role) : NULL;
|
|
|
|
|
user->profile.affiliation = affiliation && strcmp(affiliation, "none") != 0
|
|
|
|
|
? strdup(affiliation) : NULL;
|
|
|
|
|
if (certificate && channel)
|
|
|
|
|
{
|
|
|
|
|
user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, certificate);
|
|
|
|
|
user = user__new(account, std::string(binding.from->full).data(),
|
|
|
|
|
channel && std::string(binding.from->bare).data() == channel->id
|
|
|
|
|
? (binding.from->resource.size() ? std::string(binding.from->resource).data() : "")
|
|
|
|
|
: std::string(binding.from->full).data());
|
|
|
|
|
auto status = binding.status();
|
|
|
|
|
auto show = binding.show();
|
|
|
|
|
auto idle = binding.idle_since();
|
|
|
|
|
user->profile.status_text = status ? strdup(status->data()) : NULL;
|
|
|
|
|
user->profile.status = show ? strdup(show->data()) : NULL;
|
|
|
|
|
user->profile.idle = idle ? strdup("2000-01-01T00:00:00.000z") : NULL;
|
|
|
|
|
user->is_away = show ? *show == "away" : false;
|
|
|
|
|
user->profile.role = NULL;
|
|
|
|
|
user->profile.affiliation = NULL;
|
|
|
|
|
if (channel)
|
|
|
|
|
{
|
|
|
|
|
if (auto signature = binding.signature(); signature && account->pgp)
|
|
|
|
|
{
|
|
|
|
|
user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, signature->data());
|
|
|
|
|
if (channel->type != CHANNEL_TYPE_MUC)
|
|
|
|
|
channel->pgp.id = user->profile.pgp_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channel)
|
|
|
|
|
{
|
|
|
|
|
if (weechat_strcasecmp(type, "unavailable") == 0)
|
|
|
|
|
channel__remove_member(account, channel, from, status);
|
|
|
|
|
if (user->profile.role)
|
|
|
|
|
channel__remove_member(account, channel, std::string(binding.from->full).data(), status ? status->data() : nullptr);
|
|
|
|
|
else
|
|
|
|
|
channel__add_member(account, channel, from, clientid);
|
|
|
|
|
channel__add_member(account, channel, std::string(binding.from->full).data(), clientid.data());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (clientid)
|
|
|
|
|
free(clientid);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1213,8 +1191,6 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status,
|
|
|
|
|
NULL, "presence", NULL, account);
|
|
|
|
|
xmpp_handler_add(conn, &connection__message_handler,
|
|
|
|
|
NULL, "message", /*type*/ NULL, account);
|
|
|
|
|
//xmpp_handler_add(conn, &connection__iq_handler,
|
|
|
|
|
// NULL, "iq", "get", account);
|
|
|
|
|
xmpp_handler_add(conn, &connection__iq_handler,
|
|
|
|
|
NULL, "iq", NULL, account);
|
|
|
|
|
|
|
|
|
|