Microblog (#18)

* Microblog Plugin

Display information about new blog entry
This commit is contained in:
Stefan
2020-03-02 09:12:13 +01:00
committed by GitHub
parent 55da731bf6
commit 2d9793cf32

191
development/microblog.c Normal file
View File

@@ -0,0 +1,191 @@
/*
* Copyright (C) 2020 Stefan Kropp <stefan@debxwoody.de>
*
* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !!! Status: Development / Testing !!!!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* File: microblog.c
*
* The microblog plugin can be used for Movim Users. If profanity gets a message
* "urn:xmpp:microblog:0", it will display the from, title and ID information of
* the blog post.
*
* Profanity will display a message like this, in the main Window:
*
* Blog from user@domain.tld - Title of the post (ID of the post)
*
* Compile:
*
* gcc -shared -fpic -g3 -O0 -Wextra -pedantic \
* -L/usr/lib/profanity -lprofanity `pkg-config --libs --cflags libxml-2.0` \
* -o microblog.so microblog.c
*
* Install:
*
* cp microblog.so \
* ~/.local/share/profanity/plugins/
*
* Documentation:
*
* https://profanity-im.github.io/plugins.html
*
*/
#define _GNU_SOURCE
#include <profapi.h>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PLUGIN_NAME "microblog"
#define PLUGIN_VERSION "0.0.1-DEV"
#define XMPP_URN_MICROBLOG "urn:xmpp:microblog:0"
/**
* PubSubEvent is the struct with all data of the stanza, which should be
* displayed or processed by the plugin..
*/
typedef struct {
char *id; // id of the item
char *from; // jid from
char *title; // title of the item
} PubSubEvent;
// Enable or disable Plugin
static bool enabled = true;
/**
* Parse the char* stanza and return the information in event.
* The function will return true on success, otherwise false.
*/
bool parse(const char *stanza, PubSubEvent *event) {
bool result = false;
// stanza and event are mandatory
if (stanza == NULL || event == NULL || strlen(stanza) == 0) {
return result;
}
xmlDocPtr doc;
xmlNode *root_element = NULL;
doc = xmlReadMemory(stanza, strlen(stanza), NULL, NULL, 0);
if (doc == NULL) {
return result;
}
xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
xmlXPathRegisterNs(xpathCtx, BAD_CAST "ev",
BAD_CAST "http://jabber.org/protocol/pubsub#event");
xmlXPathRegisterNs(xpathCtx, BAD_CAST "a",
BAD_CAST "http://www.w3.org/2005/Atom");
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(
"//message/ev:event/ev:items/attribute::node", xpathCtx);
if (xpathObj != NULL && xpathObj->nodesetval != NULL &&
xpathObj->nodesetval->nodeTab[0] != NULL) {
if (xpathObj->nodesetval->nodeTab[0]->children != NULL &&
xpathObj->nodesetval->nodeTab[0]->children->content != NULL) {
if (strcmp(xpathObj->nodesetval->nodeTab[0]->children->content,
XMPP_URN_MICROBLOG) == 0) {
xpathObj =
xmlXPathEvalExpression("//message/attribute::from", xpathCtx);
assert(xpathObj != NULL);
event->from =
strdup(xpathObj->nodesetval->nodeTab[0]->children->content);
xmlXPathFreeObject(xpathObj);
xpathObj = xmlXPathEvalExpression(
"//message/ev:event/ev:items/ev:item/a:entry/a:title", xpathCtx);
assert(xpathObj != NULL);
event->title =
strdup(xpathObj->nodesetval->nodeTab[0]->children->content);
xmlXPathFreeObject(xpathObj);
xpathObj = xmlXPathEvalExpression(
"//message/ev:event/ev:items/ev:item/attribute::id", xpathCtx);
assert(xpathObj != NULL);
event->id = strdup(xpathObj->nodesetval->nodeTab[0]->children->content);
xmlXPathFreeObject(xpathObj);
result = true;
}
}
}
if (xpathCtx) {
xmlXPathFreeContext(xpathCtx);
}
if (doc) {
xmlFreeDoc(doc);
}
return result;
}
void prof_init(const char *const version, const char *const status,
const char *const account_name, const char *const fulljid) {
char *s = NULL;
asprintf(&s, "Loaded Plugin %s %s", PLUGIN_NAME, PLUGIN_VERSION);
prof_cons_show(s);
free(s);
}
void prof_on_start(void) {}
void prof_on_shutdown(void) {}
void prof_on_unload(void) {
char *s = NULL;
asprintf(&s, "Unloading Plugin %s %s", PLUGIN_NAME, PLUGIN_VERSION);
prof_cons_show(s);
free(s);
}
int prof_on_message_stanza_receive(const char *const stanza) {
if (enabled) {
PubSubEvent event;
event.id = NULL;
event.from = NULL;
event.title = NULL;
if (parse(stanza, &event)) {
int size =
strlen(event.id) + strlen(event.from) + strlen(event.title) + 30;
char *message = calloc(size, sizeof(char));
snprintf(message, size, "Blog from %s - %s (%s)", event.from, event.title,
event.id);
prof_cons_show(message);
prof_cons_alert();
free(message);
free(event.id);
free(event.from);
free(event.title);
}
}
return 1;
}