/* * Copyright (C) 2020 Stefan Kropp * * 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 . * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * !!! 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 #include #include #include #include #include #include #include #include #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; }