checkpoint

master
Tony Olagbaiye 2 years ago
parent 134bd388f4
commit e8d4600ef1
No known key found for this signature in database
GPG Key ID: 9E2FF3BDEBDFC910

@ -30,7 +30,7 @@ PACKAGES=(
libsignal-protocol-c -l libomemo.scm # Dep (libsignal)
lmdb lmdbxx # Dep (lmdb)
rnp # Dep (rnpgp)
xsd # Generate xml schema code
xsd # Generate XML schema code
)
echo direnv: fetching source - weechat

@ -408,6 +408,7 @@ struct t_account *account__alloc(const char *name)
/* alloc memory for new account */
new_account = new struct t_account;
std::memset(&new_account->omemo, 0, sizeof(new_account->omemo));
if (!new_account)
{
weechat_printf(NULL,
@ -440,7 +441,17 @@ struct t_account *account__alloc(const char *name)
new_account->logger.handler = &account__log_emit_weechat;
new_account->logger.userdata = new_account;
new_account->context = xmpp_ctx_new(NULL, &new_account->logger);
new_account->memory.alloc = [](const size_t size, void *const) {
return calloc(1, size);
};
new_account->memory.free = [](void *ptr, void *const) {
free(ptr);
};
new_account->memory.realloc = [](void *ptr, const size_t size, void *const) {
return realloc(ptr, size);
};
new_account->memory.userdata = new_account;
new_account->context = xmpp_ctx_new(&new_account->memory, &new_account->logger);
new_account->connection = NULL;
new_account->buffer = NULL;
@ -742,24 +753,32 @@ int account__timer_cb(const void *pointer, void *data, int remaining_calls)
(void) data;
(void) remaining_calls;
struct t_account *ptr_account;
try
{
struct t_account *ptr_account;
if (!accounts) return WEECHAT_RC_ERROR;
if (!accounts) return WEECHAT_RC_ERROR;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account ? ptr_account->next_account : NULL)
{
if (ptr_account->is_connected
&& (xmpp_conn_is_connecting(ptr_account->connection)
|| xmpp_conn_is_connected(ptr_account->connection)))
connection__process(ptr_account->context, ptr_account->connection, 10);
else if (ptr_account->disconnected);
else if (ptr_account->reconnect_start > 0
&& ptr_account->reconnect_start < time(NULL))
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account ? ptr_account->next_account : NULL)
{
account__connect(ptr_account);
if (ptr_account->is_connected
&& (xmpp_conn_is_connecting(ptr_account->connection)
|| xmpp_conn_is_connected(ptr_account->connection)))
connection__process(ptr_account->context, ptr_account->connection, 10);
else if (ptr_account->disconnected);
else if (ptr_account->reconnect_start > 0
&& ptr_account->reconnect_start < time(NULL))
{
account__connect(ptr_account);
}
}
}
return WEECHAT_RC_OK;
return WEECHAT_RC_OK;
}
catch (const std::exception& ex)
{
__asm__("int3");
return WEECHAT_RC_ERROR;
}
}

@ -107,6 +107,7 @@ struct t_account
int reconnect_delay;
int reconnect_start;
xmpp_mem_t memory;
xmpp_log_t logger;
xmpp_ctx_t *context;
xmpp_conn_t *connection;

@ -7,6 +7,7 @@
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <regex>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
@ -152,8 +153,8 @@ struct t_gui_buffer *channel__create_buffer(struct t_account *account,
{
struct t_gui_buffer *ptr_buffer;
int buffer_created;
const char *short_name, *localvar_remote_jid;
char buffer_name[1024];
const char *short_name = NULL, *localvar_remote_jid = NULL;
char buffer_name[1024] = {0};
buffer_created = 0;
@ -1114,9 +1115,14 @@ int channel__send_message(struct t_account *account, struct t_channel *channel,
channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 0);
}
char *url = (char*)strstr(body, "http");
if (url && channel->transport == CHANNEL_TRANSPORT_PLAIN)
static const std::regex pattern("https?:[^ ]*");
std::cmatch match;
if (channel->transport == CHANNEL_TRANSPORT_PLAIN &&
std::regex_search(body, match, pattern)
&& match[0].matched && !match.prefix().length())
{
std::string url { &*match[0].first, static_cast<size_t>(match[0].length()) };
xmpp_stanza_t *message__x = xmpp_stanza_new(account->context);
xmpp_stanza_set_name(message__x, "x");
xmpp_stanza_set_ns(message__x, "jabber:x:oob");
@ -1125,7 +1131,7 @@ int channel__send_message(struct t_account *account, struct t_channel *channel,
xmpp_stanza_set_name(message__x__url, "url");
xmpp_stanza_t *message__x__url__text = xmpp_stanza_new(account->context);
xmpp_stanza_set_text(message__x__url__text, url);
xmpp_stanza_set_text(message__x__url__text, url.data());
xmpp_stanza_add_child(message__x__url, message__x__url__text);
xmpp_stanza_release(message__x__url__text);

@ -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,188 +87,164 @@ 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)
std::string clientid;
if (auto caps = binding.capabilities())
{
node = xmpp_stanza_get_attribute(pres__c, "node");
ver = xmpp_stanza_get_attribute(pres__c, "ver");
if (node && ver)
{
int len = strlen(node)+1+strlen(ver) + 1;
clientid = (char*)malloc(sizeof(char)*len);
snprintf(clientid, len, "%s#%s", node, ver);
xmpp_stanza_t *children[2] = {NULL};
children[0] = stanza__iq_pubsub_items(account->context, NULL,
const_cast<char*>("eu.siacs.conversations.axolotl.devicelist"));
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"));
xmpp_send(conn, children[0]);
xmpp_stanza_release(children[0]);
}
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,
const_cast<char*>("eu.siacs.conversations.axolotl.devicelist"));
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.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);
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))
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 (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)
switch (status)
{
const char *pres__x__status__code = xmpp_stanza_get_attribute(
pres__x__item, "code");
switch (strtol(pres__x__status__code, NULL, 10))
{
case 100: // Non-Anonymous: [message | Entering a room]: Inform user that any occupant is allowed to see the user's full JID
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
break;
case 102: // : [message | Configuration change]: Inform occupants that room now shows unavailable members
break;
case 103: // : [message | Configuration change]: Inform occupants that room now does not show unavailable members
break;
case 104: // : [message | Configuration change]: Inform occupants that a non-privacy-related room configuration change has occurred
break;
case 110: // Self-Presence: [presence | Any room presence]: Inform user that presence refers to one of its own room occupants
break;
case 170: // Logging Active: [message or initial presence | Configuration change]: Inform occupants that room logging is now enabled
break;
case 171: // : [message | Configuration change]: Inform occupants that room logging is now disabled
break;
case 172: // : [message | Configuration change]: Inform occupants that the room is now non-anonymous
break;
case 173: // : [message | Configuration change]: Inform occupants that the room is now semi-anonymous
break;
case 174: // : [message | Configuration change]: Inform occupants that the room is now fully-anonymous
break;
case 201: // : [presence | Entering a room]: Inform user that a new room has been created
break;
case 210: // Nick Modified: [presence | Entering a room]: Inform user that the service has assigned or modified the occupant's roomnick
break;
case 301: // : [presence | Removal from room]: Inform user that he or she has been banned from the room
break;
case 303: // : [presence | Exiting a room]: Inform all occupants of new room nickname
break;
case 307: // : [presence | Removal from room]: Inform user that he or she has been kicked from the room
break;
case 321: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of an affiliation change
break;
case 322: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member
break;
case 332: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of a system shutdown
break;
default:
break;
}
break;
case 101: // : [message (out of band) | Affiliation change]: Inform user that his or her affiliation changed while not in the room
break;
case 102: // : [message | Configuration change]: Inform occupants that room now shows unavailable members
break;
case 103: // : [message | Configuration change]: Inform occupants that room now does not show unavailable members
break;
case 104: // : [message | Configuration change]: Inform occupants that a non-privacy-related room configuration change has occurred
break;
case 110: // Self-Presence: [presence | Any room presence]: Inform user that presence refers to one of its own room occupants
break;
case 170: // Logging Active: [message or initial presence | Configuration change]: Inform occupants that room logging is now enabled
break;
case 171: // : [message | Configuration change]: Inform occupants that room logging is now disabled
break;
case 172: // : [message | Configuration change]: Inform occupants that the room is now non-anonymous
break;
case 173: // : [message | Configuration change]: Inform occupants that the room is now semi-anonymous
break;
case 174: // : [message | Configuration change]: Inform occupants that the room is now fully-anonymous
break;
case 201: // : [presence | Entering a room]: Inform user that a new room has been created
break;
case 210: // Nick Modified: [presence | Entering a room]: Inform user that the service has assigned or modified the occupant's roomnick
break;
case 301: // : [presence | Removal from room]: Inform user that he or she has been banned from the room
break;
case 303: // : [presence | Exiting a room]: Inform all occupants of new room nickname
break;
case 307: // : [presence | Removal from room]: Inform user that he or she has been kicked from the room
break;
case 321: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of an affiliation change
break;
case 322: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member
break;
case 332: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of a system shutdown
break;
default:
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");
user = user__search(account, from);
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)
for (auto& item : x->items)
{
user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, certificate);
if (channel->type != CHANNEL_TYPE_MUC)
channel->pgp.id = user->profile.pgp_id;
}
using xml::xep0045;
if (channel)
{
if (weechat_strcasecmp(role, "none") == 0)
channel__remove_member(account, channel, from, status);
else
channel__add_member(account, channel, from, jid ? jid : clientid);
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, 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 (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, 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);
if (channel->type != CHANNEL_TYPE_MUC)
channel->pgp.id = user->profile.pgp_id;
}
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 (weechat_strcasecmp(type, "unavailable") == 0)
channel__remove_member(account, channel, from, status);
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 (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);

@ -1,6 +1,6 @@
ifdef DEBUG
DBGCFLAGS=-fsanitize=address -fsanitize=undefined -fsanitize=leak
DBGLDFLAGS=-lasan -lubsan -llsan
DBGCFLAGS=-fno-omit-frame-pointer -fsanitize=address #-fsanitize=undefined -fsanitize=leak
DBGLDFLAGS=-lasan -lrt -lasan #-lubsan -llsan
endif
RM=rm -f
@ -8,8 +8,8 @@ FIND=find
INCLUDES=-Ilibstrophe -Ideps -Ideps/fmt/include \
$(shell xml2-config --cflags) \
$(shell pkg-config --cflags librnp-0) \
$(shell pkg-config --cflags libomemo-c)
$(shell pkg-config --cflags librnp) \
$(shell pkg-config --cflags libsignal-protocol-c)
CFLAGS+=$(DBGCFLAGS) \
-fno-omit-frame-pointer -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \
@ -23,7 +23,7 @@ CFLAGS+=$(DBGCFLAGS) \
CPPFLAGS+=$(DBGCFLAGS) -O0 \
-fno-omit-frame-pointer -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \
-std=c++20 -gdwarf-4 -fkeep-inline-functions \
-std=c++23 -gdwarf-4 -fkeep-inline-functions \
-Wall -Wextra -pedantic \
-Wno-missing-field-initializers \
$(INCLUDES)
@ -34,8 +34,8 @@ LDFLAGS+=$(DBGLDFLAGS) \
LDLIBS=-lstrophe \
-lpthread \
$(shell xml2-config --libs) \
$(shell pkg-config --libs librnp-0) \
$(shell pkg-config --libs libomemo-c) \
$(shell pkg-config --libs librnp) \
$(shell pkg-config --libs libsignal-protocol-c) \
-lgcrypt \
-llmdb
@ -92,9 +92,6 @@ weechat-xmpp: $(DEPS) xmpp.so
xmpp.so: $(OBJS) $(DEPS) $(HDRS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(DEPS) $(LDLIBS)
which patchelf >/dev/null && \
patchelf --set-rpath $(LIBRARY_PATH):$(shell realpath $(shell dirname $(shell gcc --print-libgcc-file-name))/../../../) xmpp.so && \
patchelf --shrink-rpath xmpp.so || true
.%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
@ -123,15 +120,14 @@ deps/fmt/libfmt.a:
$(MAKE) -C deps/fmt fmt
fmt: deps/fmt/libfmt.a
tests/run: $(COVS) $(DEPS) $(HDRS) tests/main.cc
tests/xmpp.cov.so: $(COVS) $(DEPS) $(HDRS)
$(CXX) --coverage -O0 $(LDFLAGS) -o tests/xmpp.cov.so $(COVS) $(DEPS) $(LDLIBS)
env --chdir tests $(CXX) $(CPPFLAGS) -o run xmpp.cov.so main.cc $(LDLIBS)
which patchelf >/dev/null && \
patchelf --set-rpath $(PWD)/tests:$(LIBRARY_PATH):$(shell realpath $(shell dirname $(shell gcc --print-libgcc-file-name))/../../../) tests/xmpp.cov.so tests/run && \
patchelf --shrink-rpath tests/run tests/xmpp.cov.so || true
tests/run: $(COVS) tests/main.cc tests/xmpp.cov.so
env --chdir tests $(CXX) $(CPPFLAGS) -o run ./xmpp.cov.so main.cc $(LDLIBS)
test: tests/run
env --chdir tests ./run
env --chdir tests ./run -s
coverage: tests/run
gcov -m -abcfu -rqk -i .*.gcda xmpp/.*.gcda
@ -160,7 +156,7 @@ tidy:
$(FIND) . -name "*.gcda" -delete
clean:
$(RM) -f $(OBJS)
$(RM) -f $(OBJS) $(COVS)
$(MAKE) -C deps/diff clean || true
$(MAKE) -C deps/fmt clean || true
git submodule foreach --recursive git clean -xfd || true

@ -61,6 +61,18 @@ size_t base64_encode(const uint8_t *buffer, size_t length, char **result)
return weechat_string_base_encode(64, (char*)buffer, length, *result);
}
std::vector<std::uint8_t> base64_decode(std::string_view buffer)
{
auto result = std::make_unique<std::uint8_t[]>(buffer.size() + 1);
return std::vector<std::uint8_t>(result.get(), result.get() + weechat_string_base_decode(64, buffer.data(), (char*)result.get()));
}
std::string base64_encode(std::vector<std::uint8_t> buffer)
{
auto result = std::make_unique<char[]>(buffer.size() * 2);
return std::string(result.get(), result.get() + weechat_string_base_encode(64, (char*)buffer.data(), buffer.size(), result.get()));
}
int aes_decrypt(const uint8_t *ciphertext, size_t ciphertext_len,
uint8_t *key, uint8_t *iv, uint8_t *tag, size_t tag_len,
uint8_t **plaintext, size_t *plaintext_len)
@ -1761,7 +1773,6 @@ std::optional<libsignal::pre_key_bundle> bks_load_bundle(struct signal_protocol_
return bundle;
}
extern "C"
void log_emit_weechat(int level, const char *message, size_t len, void *user_data)
{
struct t_gui_buffer *buffer = (struct t_gui_buffer*)user_data;

@ -6,6 +6,7 @@
#include <cstdint>
#include <cstring>
#include <ctime>
#include <csignal>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
@ -34,12 +35,20 @@ WEECHAT_PLUGIN_LICENSE("MPL2");
WEECHAT_PLUGIN_PRIORITY(5500);
}
extern "C"
void weechat_signal_handler(int)
{
__asm__("int3");
}
extern "C"
int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[])
{
(void) argc;
(void) argv;
std::signal(SIGSEGV, weechat_signal_handler);
weechat_xmpp_plugin = plugin;
if (!config__init())

@ -1,3 +1,5 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
#include "plugin.inl"

@ -1,18 +1,14 @@
#include <doctest/doctest.h>
#include <weechat/weechat-plugin.h>
#include "../plugin.hh"
TEST_CASE("placeholder")
TEST_CASE("weechat")
{
int argc = 2;
const char *argv[2] = {"a", "b"};
std::string current("20211106-01");
SUBCASE("takes no arguments")
SUBCASE("plugin api match")
{
CHECK(argc != 1);
CHECK(current == WEECHAT_PLUGIN_API_VERSION);
}
(void) argv;
//weechat::plugin c;
//CHECK(&c.name() == NULL);
}

@ -2,6 +2,8 @@
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <ctime>
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
@ -36,54 +38,40 @@ std::string get_text(xmpp_stanza_t *stanza) {
std::chrono::system_clock::time_point get_time(const std::string& text) {
std::tm tm = {};
std::istringstream ss(text);
//ss.imbue(std::locale("en_GB.utf-8"));
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S%z");
if (ss.fail()) {
if (strptime(text.data(), "%FT%T%z", &tm)) {
throw std::invalid_argument("Bad time format");
} else {
return std::chrono::system_clock::from_time_t(std::mktime(&tm));
}
}
jid::jid(xmpp_ctx_t *context, std::string s) {
char *result = NULL;
result = xmpp_jid_node(context, s.data());
if (result)
{
local = result;
xmpp_free(context, result);
result = NULL;
}
result = xmpp_jid_domain(context, s.data());
if (result)
const std::regex jid::pattern(
"^((?:([^@/<>'\"]+)@)?([^@/<>'\"]+))(?:/([^<>'\"]*))?$");
jid::jid(xmpp_ctx_t *, std::string s) : full(s) {
auto as_sv = [](std::ssub_match m) {
if(!m.matched) return std::string_view();
return std::string_view { &*m.first, static_cast<size_t>(m.length()) };
};
std::smatch match;
if (std::regex_search(full, match, pattern))
{
domain = result;
xmpp_free(context, result);
result = NULL;
bare = as_sv(match[1]);
local = as_sv(match[2]);
domain = as_sv(match[3]);
resource = as_sv(match[4]);
}
else
throw std::invalid_argument("Invalid JID");
result = xmpp_jid_resource(context, s.data());
if (result)
{
resource = result;
xmpp_free(context, result);
result = NULL;
bare = full;
domain = bare;
}
}
std::string jid::full() const {
return fmt::format("{}{}{}{}{}", *local, local ? "@" : "", domain,
resource ? "/" : "", *resource);
}
std::string jid::bare() const {
return fmt::format("{}{}{}", *local, local ? "@" : "", domain);
}
bool jid::is_bare() const {
return !resource;
return !resource.empty();
}
xml::node::node() {}
@ -164,3 +152,14 @@ void xml::iq::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
node::bind(context, stanza);
}
void xml::error::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
auto result = get_attribute(stanza, "from");
if (result)
from = jid(context, *result);
result = get_attribute(stanza, "to");
if (result)
to = jid(context, *result);
node::bind(context, stanza);
}

@ -7,14 +7,12 @@
#include <map>
#include <memory>
#include <optional>
#include <regex>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include <chrono>
#include <sstream>
#include <locale>
#include <iomanip>
#include <fmt/core.h>
#include <strophe.h>
@ -27,15 +25,20 @@ std::string get_text(xmpp_stanza_t *stanza);
std::chrono::system_clock::time_point get_time(const std::string& text);
class jid {
public:
std::optional<std::string> local;
std::string domain;
std::optional<std::string> resource;
private:
static const std::regex pattern;
public:
jid(xmpp_ctx_t *context, std::string s);
std::string full() const;
std::string bare() const;
operator std::string&() { return full; }
std::string full;
std::string_view bare;
std::string_view local;
std::string_view domain;
std::string_view resource;
bool is_bare() const;
};
@ -46,10 +49,12 @@ namespace xml {
explicit node();
public:
inline node(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
inline node(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : context(context) {
bind(context, stanza);
}
xmpp_ctx_t *context;
std::optional<std::string> name;
std::optional<std::string> id;
@ -62,6 +67,14 @@ namespace xml {
virtual void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza);
inline std::optional<std::string>
get_attr(const std::string& name) {
auto attribute = attributes.find(name);
if (attribute != attributes.end())
return attribute->second;
return {};
}
template<typename xmlns>
inline std::vector<std::reference_wrapper<node>>
get_children(std::string_view name) {
@ -96,9 +109,12 @@ namespace xml {
namespace xml {
class message : virtual public node, public xep0027 {
class message : virtual public node,
public xep0027 {
public:
using node::node;
inline message(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) {
bind(context, stanza);
}
std::optional<jid> from;
std::optional<jid> to;
@ -108,9 +124,12 @@ namespace xml {
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
class presence : virtual public node, public xep0045, public xep0115, public xep0319 {
class presence : virtual public node,
public xep0027, public xep0045, public xep0115, public xep0319 {
public:
using node::node;
inline presence(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) {
bind(context, stanza);
}
std::optional<jid> from;
std::optional<jid> to;
@ -125,7 +144,9 @@ namespace xml {
class iq : virtual public node {
public:
using node::node;
inline iq(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) {
bind(context, stanza);
}
std::optional<jid> from;
std::optional<jid> to;
@ -135,4 +156,16 @@ namespace xml {
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
class error : virtual public node {
public:
inline error(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) {
bind(context, stanza);
}
std::optional<jid> from;
std::optional<jid> to;
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
}

@ -15,19 +15,32 @@
namespace xml {
class xep0027 : virtual public node {
private:
std::optional<std::optional<std::string>> _signature;
std::optional<std::optional<std::string>> _encrypted;
public:
std::optional<std::string> signature() {
auto child = get_children<jabber::x::signed_>("x");
if (child.size() > 0)
return child.front().get().text;
return {};
std::optional<std::string>& signature() {
if (!_signature)
{
auto child = get_children<jabber::x::signed_>("x");
if (child.size() > 0)
_signature = child.front().get().text;
else
_signature.emplace(std::nullopt);
}
return *_signature;
}
std::optional<std::string> encrypted() {
auto child = get_children<jabber::x::encrypted>("x");
if (child.size() > 0)
return child.front().get().text;
return {};
std::optional<std::string>& encrypted() {
if (!_encrypted)
{
auto child = get_children<jabber::x::encrypted>("x");
if (child.size() > 0)
_encrypted = child.front().get().text;
else
_encrypted.emplace(std::nullopt);
}
return *_encrypted;
}
};

@ -5,7 +5,10 @@
#pragma once
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
#include <fmt/core.h>
#include "node.hh"
#pragma GCC visibility push(default)
@ -16,31 +19,193 @@ namespace xml {
class xep0045 : virtual public node {
public:
enum class affiliation {
admin,
member,
none,
outcast,
owner,
};
enum class role {
moderator,
none,
participant,
visitor,
};
static affiliation parse_affiliation(std::string_view s) {
if (s == "admin")
return affiliation::admin;
else if (s == "member")
return affiliation::member;
else if (s == "none")
return affiliation::none;
else if (s == "outcast")
return affiliation::outcast;
else if (s == "owner")
return affiliation::owner;
throw std::invalid_argument(
fmt::format("Bad affiliation: {}", s));
}
static std::string_view format_affiliation(affiliation e) {
switch (e) {
case affiliation::admin:
return "admin";
case affiliation::member:
return "member";
case affiliation::none:
return "none";
case affiliation::outcast:
return "outcast";
case affiliation::owner:
return "owner";
default:
return "";
}
}
static role parse_role(std::string_view s) {
if (s == "moderator")
return role::moderator;
else if (s == "none")
return role::none;
else if (s == "participant")
return role::participant;
else if (s == "visitor")
return role::visitor;
throw std::invalid_argument(
fmt::format("Bad role: {}", s));
}
static std::string_view format_role(role e) {
switch (e) {
case role::moderator:
return "moderator";
case role::none:
return "none";
case role::participant:
return "participant";
case role::visitor:
return "visitor";
default:
return "";
}
}
class x {
private:
struct decline {
decline(node& node) {
for (auto& child : node.get_children("reason"))
reason += child.get().text;
if (auto attr = node.get_attr("from"))
from = jid(node.context, *attr);
if (auto attr = node.get_attr("to"))
to = jid(node.context, *attr);
};
std::string reason;
std::optional<jid> from;
std::optional<jid> to;
};
struct destroy {
destroy(node& node) {
for (auto& child : node.get_children("reason"))
reason += child.get().text;
if (auto attr = node.get_attr("target"))
target = jid(node.context, *attr);
};
std::string reason;
std::optional<jid> target;
};
struct invite {
invite(node& node) {
for (auto& child : node.get_children("reason"))
reason += child.get().text;
if (auto attr = node.get_attr("from"))
from = jid(node.context, *attr);
if (auto attr = node.get_attr("to"))
to = jid(node.context, *attr);
};
std::string reason;
std::optional<jid> from;
std::optional<jid> to;
};
struct item {
class item {
private:
struct actor {
actor(node& node) {
for (auto& child : node.get_children("reason"))
reason += child.get().text;
if (auto attr = node.get_attr("jid"))
target = jid(node.context, *attr);
if (auto attr = node.get_attr("nick"))
nick = *attr;
}
std::string reason;
std::optional<jid> target;
std::string nick;
};
struct continue_ {
continue_(node& node) {
if (auto attr = node.get_attr("thread"))
thread = *attr;
}
std::string thread;
};
public:
item(node& node) {
for (auto& child : node.get_children("actor"))
actors.emplace_back(child);
for (auto& child : node.get_children("continue"))
continues.emplace_back(child);
for (auto& child : node.get_children("reason"))
reason += child.get().text;
if (auto attr = node.get_attr("affiliation"))
affiliation = parse_affiliation(*attr);
if (auto attr = node.get_attr("jid"))
target = jid(node.context, *attr);
if (auto attr = node.get_attr("nick"))
nick = *attr;
if (auto attr = node.get_attr("role"))
role = parse_role(*attr);
};
std::vector<actor> actors;
std::vector<continue_> continues;
std::string reason;
std::optional<enum affiliation> affiliation;
std::optional<jid> target;
std::optional<std::string> nick;
std::optional<enum role> role;
};
public:
x(const node& node) {
x(node& node) {
for (auto& child : node.get_children("decline"))
declines.emplace_back(child);
for (auto& child : node.get_children("destroy"))
destroys.emplace_back(child);
for (auto& child : node.get_children("invite"))
invites.emplace_back(child);
for (auto& child : node.get_children("item"))
items.emplace_back(child);
for (auto& child : node.get_children("password"))
passwords.emplace_back(child.get().text);
for (auto& child : node.get_children("status"))
if (auto code = child.get().get_attr("code"))
statuses.push_back(std::stoi(*code));
}
std::vector<decline> declines;
@ -51,11 +216,19 @@ namespace xml {
std::vector<int> statuses;
};
std::optional<x> muc_user() {
auto child = get_children<jabber_org::protocol::muc::user>("x");
if (child.size() > 0)
return child.front().get();
return {};
private:
std::optional<std::optional<x>> _muc_user;
public:
std::optional<x>& muc_user() {
if (!_muc_user)
{
auto child = get_children<jabber_org::protocol::muc::user>("x");
if (child.size() > 0)
_muc_user = child.front().get();
else
_muc_user.emplace(std::nullopt);
}
return *_muc_user;
}
};

@ -16,11 +16,36 @@ namespace xml{
class xep0115 : virtual public node {
public:
std::optional<node> caps() {
auto child = get_children<jabber_org::protocol::caps>("c");
if (child.size() > 0)
return child.front().get();
return {};
struct caps {
caps(::xml::node& node) {
if (auto attr = node.get_attr("ext"))
ext = *attr;
if (auto attr = node.get_attr("hash"))
hashalgo = *attr;
if (auto attr = node.get_attr("node"))
this->node = *attr;
if (auto attr = node.get_attr("ver"))
verification = *attr;
};
std::optional<std::string> ext;
std::string hashalgo;
std::string node;
std::string verification;
};
private:
std::optional<std::optional<caps>> _capabilities;
public:
std::optional<caps> capabilities() {
if (!_capabilities)
{
auto child = get_children<jabber_org::protocol::caps>("c");
if (child.size() > 0)
_capabilities = caps(child.front().get());
_capabilities.emplace(std::nullopt);
}
return *_capabilities;
}
};

@ -17,21 +17,30 @@
namespace xml {
class xep0319 : virtual public node {
private:
std::optional<std::optional<std::chrono::system_clock::time_point>> _idle_since;
public:
std::optional<std::chrono::system_clock::time_point> idle_since() {
auto children = get_children<urn::xmpp::idle::_1>("idle");
if (children.size() <= 0)
return {};
auto child = children.front().get();
auto since = child.attributes.find("since");
if (since == child.attributes.end())
return {};
try {
return get_time(since->second);
}
catch (const std::invalid_argument& ex) {
return {};
if (!_idle_since)
{
auto children = get_children<urn::xmpp::idle::_1>("idle");
if (children.size() <= 0)
_idle_since.emplace(std::nullopt);
else {
auto since = children.front().get().get_attr("since");
if (!since)
_idle_since.emplace(std::nullopt);
else {
try {
_idle_since = get_time(*since);
}
catch (const std::invalid_argument& ex) {
_idle_since.emplace(std::nullopt);
}
}
}
}
return *_idle_since;
}
};

Loading…
Cancel
Save