sexps in /xml

master
bqv 2 years ago
parent efffd451fa
commit 40af811d42
No known key found for this signature in database
GPG Key ID: 9E2FF3BDEBDFC910

@ -282,7 +282,7 @@ struct t_channel *channel__new(struct t_account *account,
enum t_channel_type type,
const char *id, const char *name)
{
struct t_channel *new_channel, *ptr_channel;
struct t_channel *new_channel, *ptr_channel, *muc_channel;
struct t_gui_buffer *ptr_buffer;
struct t_hook *typing_timer, *self_typing_timer;
@ -300,10 +300,10 @@ struct t_channel *channel__new(struct t_account *account,
return NULL;
else if (type == CHANNEL_TYPE_PM)
{
ptr_channel = channel__search(account, jid(account->context, id).bare.data());
if (ptr_channel)
muc_channel = channel__search(account, jid(account->context, id).bare.data());
if (muc_channel)
{
weechat_buffer_merge(ptr_buffer, ptr_channel->buffer);
weechat_buffer_merge(ptr_buffer, muc_channel->buffer);
}
}
@ -893,11 +893,7 @@ void channel__update_topic(struct t_channel *channel,
void channel__update_name(struct t_channel *channel,
const char* name)
{
if (channel->name)
free(channel->name);
channel->name = (name) ? strdup(name) : NULL;
if (channel->name)
if (name)
weechat_buffer_set(channel->buffer, "short_name", name);
else
weechat_buffer_set(channel->buffer, "short_name", "");
@ -1149,6 +1145,23 @@ int channel__send_message(struct t_account *account, struct t_channel *channel,
{
std::string url { &*match[0].first, static_cast<size_t>(match[0].length()) };
//struct t_hashtable *options = weechat_hashtable_new (8,
// WEECHAT_HASHTABLE_STRING,
// WEECHAT_HASHTABLE_STRING,
// NULL,
// NULL);
//if (!options) { return; }
//weechat_hashtable_set(options, "nobody", "1");
//auto command = "url:" + url;
//const int timeout = 30000;
//struct t_hook *process_hook =
// weechat_hook_process_hashtable(command, options, timeout,
// int (*callback)(const void *pointer, void *data,
// const char *command,
// int return_code, const char *out, const char *err),
// const void *callback_pointer, void *callback_data);
//weechat_hashtable_free(options);
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");

@ -8,6 +8,8 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#include <sstream>
#include <weechat/weechat-plugin.h>
#include "plugin.hh"
@ -17,6 +19,7 @@
#include "buffer.hh"
#include "message.hh"
#include "command.hh"
#include "sexp/driver.hh"
#define MAM_DEFAULT_DAYS 2
#define STR(X) #X
@ -902,17 +905,41 @@ int command__xml(const void *pointer, void *data,
if (argc > 1)
{
stanza = xmpp_stanza_new_from_string(ptr_account->context,
argv_eol[1]);
if (!stanza)
auto parse = [&](sexp::driver& sxml) {
std::stringstream ss;
std::string line;
try {
return sxml.parse(argv_eol[1], &ss);
}
catch (const std::invalid_argument& ex) {
while (std::getline(ss, line))
weechat_printf(nullptr, "%ssxml: %s", weechat_prefix("info"), line.data());
weechat_printf(nullptr, "%ssxml: %s", weechat_prefix("error"), ex.what());
return false;
}
};
if (sexp::driver sxml(ptr_account->context); parse(sxml))
{
weechat_printf(nullptr, _("%s%s: Bad XML"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_ERROR;
for (auto *stanza : sxml.elements)
{
xmpp_send(ptr_account->connection, stanza);
xmpp_stanza_release(stanza);
}
}
else
{
stanza = xmpp_stanza_new_from_string(ptr_account->context,
argv_eol[1]);
if (!stanza)
{
weechat_printf(nullptr, _("%s%s: Bad XML"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_ERROR;
}
xmpp_send(ptr_account->connection, stanza);
xmpp_stanza_release(stanza);
xmpp_send(ptr_account->connection, stanza);
xmpp_stanza_release(stanza);
}
}
return WEECHAT_RC_OK;

@ -974,36 +974,40 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd
if (weechat_strcasecmp(type, "result") == 0)
{
xmpp_stanza_t *identity = xmpp_stanza_get_child_by_name(query, "identity");
std::string category;
std::string name;
std::string type;
if (const char *attr = xmpp_stanza_get_attribute(identity, "category"))
category = attr;
if (const char *attr = xmpp_stanza_get_attribute(identity, "name"))
name = unescape(attr);
if (const char *attr = xmpp_stanza_get_attribute(identity, "type"))
type = attr;
if (category == "conference")
{
struct t_channel *ptr_channel = channel__search(account, from);
if (ptr_channel)
channel__update_name(ptr_channel, name.data());
}
else if (category == "conference")
if (identity)
{
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,
binding.from ? strdup(binding.from->bare.data()) : NULL, strdup("get"));
xmpp_send(conn, children[0]);
xmpp_stanza_release(children[0]);
std::string category;
std::string name;
std::string type;
if (const char *attr = xmpp_stanza_get_attribute(identity, "category"))
category = attr;
if (const char *attr = xmpp_stanza_get_attribute(identity, "name"))
name = unescape(attr);
if (const char *attr = xmpp_stanza_get_attribute(identity, "type"))
type = attr;
if (category == "conference")
{
struct t_channel *ptr_channel = channel__search(account, from);
if (ptr_channel)
channel__update_name(ptr_channel, name.data());
}
else if (category == "conference")
{
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,
binding.from ? strdup(binding.from->bare.data()) : NULL, strdup("get"));
xmpp_send(conn, children[0]);
xmpp_stanza_release(children[0]);
}
}
}
}

@ -39,28 +39,28 @@ LDLIBS=-lstrophe \
$(shell pkg-config --libs gpgme) \
$(shell pkg-config --libs libsignal-protocol-c) \
-lgcrypt \
-llmdb
-llmdb -lfl
PREFIX ?= /usr/local
LIBDIR ?= $(PREFIX)/lib
HDRS=plugin.hh \
account.hh \
buffer.hh \
channel.hh \
command.hh \
completion.hh \
config.hh \
connection.hh \
input.hh \
message.hh \
omemo.hh \
pgp.hh \
user.hh \
util.hh \
xmpp/stanza.hh \
xmpp/ns.hh \
xmpp/node.hh \
account.hh \
buffer.hh \
channel.hh \
command.hh \
completion.hh \
config.hh \
connection.hh \
input.hh \
message.hh \
omemo.hh \
pgp.hh \
user.hh \
util.hh \
xmpp/stanza.hh \
xmpp/ns.hh \
xmpp/node.hh \
SRCS=plugin.cpp \
account.cpp \
@ -82,16 +82,19 @@ SRCS=plugin.cpp \
DEPS=deps/diff/libdiff.a \
deps/fmt/libfmt.a \
sexp/sexp.a \
OBJS=$(patsubst %.cpp,.%.o,$(patsubst %.c,.%.o,$(patsubst xmpp/%.cpp,xmpp/.%.o,$(SRCS))))
COVS=$(patsubst %.cpp,.%.cov.o,$(patsubst xmpp/%.cpp,xmpp/.%.cov.o,$(SRCS)))
SUFFIX=$(shell date +%s)
.PHONY: all
all:
make depend
make weechat-xmpp && make test
.PHONY: weechat-xmpp release
weechat-xmpp: $(DEPS) xmpp.so
release: xmpp.so
cp xmpp.so .xmpp.so.$(SUFFIX)
@ -102,6 +105,20 @@ xmpp.so: $(OBJS) $(DEPS) $(HDRS)
git ls-files | xargs ls -d | xargs tar cz | objcopy --add-section .source=/dev/stdin xmpp.so
#objcopy --dump-section .source=/dev/stdout xmpp.so | tar tz
sexp/sexp.a: sexp/driver.o sexp/parser.o sexp/lexer.o
ar -r $@ $^
sexp/parser.o: sexp/parser.yy
cd sexp && bison -t -d -v parser.yy
$(CXX) $(CPPFLAGS) -c sexp/parser.tab.cc -o $@
sexp/lexer.o: sexp/lexer.l
cd sexp && flex -d --outfile=lexer.yy.cc lexer.l
$(CXX) $(CPPFLAGS) -c sexp/lexer.yy.cc -o $@
sexp/driver.o: sexp/driver.cpp
$(CXX) $(CPPFLAGS) -c $< -o $@
.%.o: %.c
$(eval GIT_REF=$(shell git describe --abbrev=6 --always --dirty 2>/dev/null || true))
$(CC) -DGIT_COMMIT=$(GIT_REF) $(CFLAGS) -c $< -o $@
@ -119,6 +136,7 @@ xmpp/.%.o: xmpp/%.cpp
xmpp/.%.cov.o: xmpp/%.cpp
@$(CXX) --coverage $(CPPFLAGS) -O0 -c $< -o $@
.PHONY: diff fmt
deps/diff/libdiff.a:
git submodule update --init --recursive
cd deps/diff && env -u MAKEFLAGS ./configure
@ -137,16 +155,20 @@ tests/xmpp.cov.so: $(COVS) $(DEPS) $(HDRS)
tests/run: $(COVS) tests/main.cc tests/xmpp.cov.so
env --chdir tests $(CXX) $(CPPFLAGS) -o run ./xmpp.cov.so main.cc $(LDLIBS)
.PHONY: test
test: tests/run
env --chdir tests ./run -s
.PHONY: coverage
coverage: tests/run
gcov -m -abcfu -rqk -i .*.gcda xmpp/.*.gcda
.PHONY: debug
debug: xmpp.so
env LD_PRELOAD=$(DEBUG) gdb -ex "handle SIGPIPE nostop noprint pass" --args \
weechat -a -P 'alias,buflist,exec,irc,relay' -r '/plugin load ./xmpp.so'
.PHONY: depend
depend: $(SRCS) $(HDRS)
$(RM) -f ./.depend
echo > ./.depend
@ -161,21 +183,29 @@ depend: $(SRCS) $(HDRS)
done
sed -i 's/\.\([a-z]*\/\)/\1./' .depend
.PHONY: tidy
tidy:
$(FIND) . -name "*.o" -delete
$(FIND) . -name "*.gcno" -delete
$(FIND) . -name "*.gcda" -delete
.PHONY: clean
clean:
$(RM) -f $(OBJS) $(COVS)
$(RM) -f $(OBJS) $(COVS) \
sexp/parser.tab.cc sexp/parser.tab.hh \
sexp/location.hh sexp/position.hh \
sexp/stack.hh sexp/parser.output sexp/parser.o \
sexp/lexer.o sexp/lexer.yy.cc sexp/sexp.a
$(MAKE) -C deps/diff clean || true
$(MAKE) -C deps/fmt clean || true
git submodule foreach --recursive git clean -xfd || true
git submodule foreach --recursive git reset --hard || true
.PHONY: distclean
distclean: clean
$(RM) *~ .depend
.PHONY: install
install: xmpp.so
ifeq ($(shell id -u),0)
mkdir -p $(DESTDIR)$(LIBDIR)/weechat/plugins
@ -187,8 +217,7 @@ else
chmod 755 ~/.weechat/plugins/xmpp.so
endif
.PHONY: all weechat-xmpp release test debug depend tidy clean distclean install check
.PHONY: check
check:
clang-check --analyze *.c *.cc *.cpp

@ -0,0 +1,93 @@
#include <cctype>
#include <sstream>
#include <cassert>
#include <exception>
#include "driver.hh"
sexp::driver::~driver()
{
delete(scanner);
scanner = nullptr;
delete(parser);
parser = nullptr;
}
bool sexp::driver::parse(const char *text, std::ostream *debug = nullptr)
{
assert(text != nullptr);
std::istringstream stream{std::string(text)};
return parse(stream, debug);
}
bool sexp::driver::parse(std::istream &stream, std::ostream *debug = nullptr)
{
if(!stream.good() && stream.eof())
{
return false;
}
return parse_helper(stream, debug);
}
bool sexp::driver::parse_helper(std::istream &stream, std::ostream *debug)
{
delete(scanner);
try
{
scanner = new sexp::scanner(&stream);
}
catch(std::bad_alloc &ba)
{
throw std::runtime_error("Failed to allocate scanner");
}
delete(parser);
try
{
parser = new sexp::parser((*scanner) /* scanner */,
(*this) /* driver */);
}
catch(std::bad_alloc &ba)
{
throw std::runtime_error("Failed to allocate parser");
}
const int accept = 0;
if (debug)
{
parser->set_debug_level(1);
parser->set_debug_stream(*debug);
}
return parser->parse() == accept;
}
void sexp::driver::start_tag(const std::string &name)
{
auto *stanza = xmpp_stanza_new(context);
xmpp_stanza_set_name(stanza, name.data());
stack.push_back(stanza);
}
void sexp::driver::end_tag()
{
auto *stanza = stack.back();
stack.pop_back();
if (stack.empty())
elements.push_back(stanza);
else
xmpp_stanza_add_child_ex(stack.back(), stanza, false);
}
void sexp::driver::add_text(const std::string &text)
{
auto *stanza = xmpp_stanza_new(context);
xmpp_stanza_set_text(stanza, text.substr(1,text.length()-2).data());
xmpp_stanza_add_child_ex(stack.back(), stanza, false);
}
void sexp::driver::add_attr(const std::string &name, const std::string &value)
{
xmpp_stanza_set_attribute(stack.back(), name.data(),
value.substr(1,value.length()-2).data());
}

@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <cstddef>
#include <istream>
#include <vector>
#include <strophe.h>
#include "scanner.hh"
#include "parser.tab.hh"
namespace sexp {
class driver {
public:
driver(xmpp_ctx_t *context) : context(context) {}
virtual ~driver();
/**
* parse - parse from a file
* @param text - valid string
*/
bool parse(const char *text, std::ostream *debug);
/**
* parse - parse from a c++ input stream
* @param is - std::istream&, valid input stream
*/
bool parse(std::istream &iss, std::ostream *debug);
void start_tag(const std::string &name);
void end_tag();
void add_text(const std::string &text);
void add_attr(const std::string &name, const std::string &value);
std::vector<xmpp_stanza_t*> elements;
private:
bool parse_helper(std::istream &stream, std::ostream *debug);
xmpp_ctx_t *context;
std::vector<xmpp_stanza_t*> stack;
sexp::parser *parser = nullptr;
sexp::scanner *scanner = nullptr;
};
}

@ -0,0 +1,56 @@
%option debug
%option nodefault
%option yyclass="sexp::scanner"
%option noyywrap
%option c++
%{
#include <string>
#include "scanner.hh"
#undef YY_DECL
#define YY_DECL int sexp::scanner::yylex(sexp::parser::semantic_type *const lval, sexp::parser::location_type *loc)
using token = sexp::parser::token;
// defaults to NULL
#define yyterminate() return token::END
// update location on matching
#define YY_USER_ACTION loc->step(); loc->columns(yyleng);
%}
/* Regular Expressions */
%%
%{
yylval = lval;
%}
"(" {
return token::LPAREN;
}
")" {
return token::RPAREN;
}
[\n \t\r]+ {
// Update line number
loc->lines();
return token::SPACE;
}
\"([^"\\]|\\.)*\" {
yylval->build<std::string>(yytext);
return token::STRING;
}
[^ "(:@)]+ {
yylval->build<std::string>(yytext);
return token::NAME;
}
. {
return *yytext;
}
%%

@ -0,0 +1,78 @@
%skeleton "lalr1.cc"
%require "3.0"
%debug
%defines
%define api.namespace {sexp}
%define api.parser.class {parser}
%code requires{
namespace sexp {
class driver;
class scanner;
}
}
%parse-param { sexp::scanner &scanner }
%parse-param { sexp::driver &driver }
%code{
#include <iostream>
#include <cstdlib>
#include <sstream>
/* include for all driver functions */
#include "driver.hh"
#undef yylex
#define yylex scanner.yylex
}
%define api.value.type variant
%define parse.assert
%define parse.error verbose
%token END 0 "end of file"
%token LPAREN
%token RPAREN
%token <std::string> NAME
%token <std::string> STRING
%token SPACE
%locations
%start input
%%
input : ws END | element input;
element : lparen attributeset rparenq
| lparen tag children rparen;
ws : | gap;
gap : SPACE;
lparen : ws LPAREN;
rparen : ws RPAREN { driver.end_tag(); };
rparenq : ws RPAREN;
tag : ws NAME { driver.start_tag($2); }
| ws NAME ':' NAME { driver.start_tag($4); };
attributeset : '@' attributes;
attributes : ws | gap attribute attributes;
attribute : lparen ws NAME gap STRING rparenq { driver.add_attr($3, $5); };
children : ws
| gap STRING children { driver.add_text($2); }
| gap element children;
%%
void sexp::parser::error(const sexp::parser::location_type &l, const std::string &err_message)
{
std::ostringstream ss;
ss << "parsing " << err_message << " at " << l;
throw std::invalid_argument(ss.str());
}

@ -0,0 +1,37 @@
#pragma once
#ifndef yyFlexLexer
#include <FlexLexer.h>
#endif
#include "parser.tab.hh"
#include "location.hh"
namespace sexp {
class scanner : public yyFlexLexer {
public:
scanner(std::istream *in) : yyFlexLexer(in)
{
};
virtual ~scanner() {
};
// get rid of override virtual function warning
using FlexLexer::yylex;
virtual
int yylex(sexp::parser::semantic_type *const lval,
sexp::parser::location_type *location);
// YY_DECL defined in lexer.l
// Method body created by flex in lexer.yy.cc
private:
/* yyval ptr */
sexp::parser::semantic_type *yylval = nullptr;
};
}
Loading…
Cancel
Save