summaryrefslogtreecommitdiff
path: root/gentoobrowse-api/service/notifications
diff options
context:
space:
mode:
Diffstat (limited to 'gentoobrowse-api/service/notifications')
-rw-r--r--gentoobrowse-api/service/notifications/Jamfile.jam44
-rw-r--r--gentoobrowse-api/service/notifications/mailserverimpl.cpp64
-rw-r--r--gentoobrowse-api/service/notifications/mailserverimpl.h20
-rw-r--r--gentoobrowse-api/service/notifications/notifications.ice49
-rw-r--r--gentoobrowse-api/service/notifications/notificationsimpl.cpp59
-rw-r--r--gentoobrowse-api/service/notifications/notificationsimpl.h29
-rw-r--r--gentoobrowse-api/service/notifications/xslt/base.xslt61
-rw-r--r--gentoobrowse-api/service/notifications/xslt/news.xslt57
-rw-r--r--gentoobrowse-api/service/notifications/xslt/signup.xslt24
-rw-r--r--gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp82
-rw-r--r--gentoobrowse-api/service/notifications/xsltStreamSerializer.h28
11 files changed, 517 insertions, 0 deletions
diff --git a/gentoobrowse-api/service/notifications/Jamfile.jam b/gentoobrowse-api/service/notifications/Jamfile.jam
new file mode 100644
index 0000000..a168649
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/Jamfile.jam
@@ -0,0 +1,44 @@
+import type : register ;
+import generators : register-standard ;
+
+type.register XSLT : xslt ;
+
+generators.register-standard xslt.h : XSLT : H ;
+
+h base-xslt : xslt/base.xslt : <slicer>yes ;
+h signup-xslt : xslt/signup.xslt : <slicer>yes ;
+h news-xslt : xslt/news.xslt : <slicer>yes ;
+
+lib exslt ;
+lib xslt : : : : <include>/usr/include/libxslt ;
+lib xml2 : : : : <include>/usr/include/libxml2 ;
+lib esmtp ;
+lib slicer-xml ;
+lib boost_system ;
+
+lib gentoobrowse-service-notifications :
+ [ glob *.ice *.cpp ]
+ :
+ <slicer>yes
+ <library>xslt
+ <library>xml2
+ <library>esmtp
+ <library>../../..//libxmlpp
+ <library>boost_system
+ <library>slicer-xml
+ <library>../../domain//gentoobrowse-domain
+ <implicit-dependency>../../domain//gentoobrowse-domain
+ <dependency>base-xslt
+ <dependency>signup-xslt
+ <dependency>news-xslt
+ : :
+ <include>.
+ ;
+
+actions xslt.h
+{
+ ( cd $(2:D) ; xxd -i $(2:B)$(2:S) ) > $(1)
+}
+
+IMPORT $(__name__) : xslt.h : : xslt.h ;
+
diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.cpp b/gentoobrowse-api/service/notifications/mailserverimpl.cpp
new file mode 100644
index 0000000..c3dfdfa
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/mailserverimpl.cpp
@@ -0,0 +1,64 @@
+#include "mailserverimpl.h"
+#include <libesmtp.h>
+#include <Ice/ObjectAdapter.h>
+#include <Ice/Communicator.h>
+
+namespace Gentoo {
+ namespace Service {
+ typedef std::vector<std::string> Parts;
+ typedef std::pair<Parts::iterator, Parts::const_iterator> PartsProgress;
+
+ void
+ MailServer::sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current & c)
+ {
+ auto props = c.adapter->getCommunicator()->getProperties();
+ smtp_session_t session = smtp_create_session();
+ smtp_message_t message = smtp_add_message(session);
+ auto server = props->getPropertyWithDefault("GentooBrowseAPI.MailServer", "localhost");
+ smtp_set_server(session, server.c_str());
+ smtp_set_header(message, "To", msg->to.name.c_str(), msg->to.address.c_str());
+ smtp_set_header(message, "From", msg->from.name.c_str(), msg->from.address.c_str());
+ smtp_set_header(message, "Subject", msg->subject.c_str());
+ Parts parts;
+ parts.emplace_back("Content-Type: multipart/alternative; boundary=\"<<divider>>\"\r\n");
+ parts.emplace_back("MIME-Version: 1.0\r\n");
+ parts.emplace_back("Content-Transfer-Encoding: binary\r\n");
+ smtp_add_recipient(message, msg->to.address.c_str());
+ for (const auto & p : msg->body) {
+ parts.emplace_back("\r\n--<<divider>>\r\nContent-Type: " + p->mimetype + "; utf-8\r\n\r\n");
+ parts.emplace_back(p->payload);
+ }
+ parts.emplace_back("\r\n--<<divider>>--\r\n");
+ PartsProgress pp { parts.begin(), parts.end() };
+ smtp_set_messagecb(message, writeBody, &pp);
+ if (!smtp_start_session(session)) {
+ char buf[BUFSIZ];
+ auto b = smtp_strerror(smtp_errno(), buf, sizeof(buf));
+ assert(b);
+ SendEmailFailed e(b);
+ smtp_destroy_session(session);
+ throw e;
+ }
+ smtp_destroy_session(session);
+ }
+
+ const char *
+ MailServer::writeBody(void **, int * len, void * arg)
+ {
+ auto parts = static_cast<PartsProgress *>(arg);
+ if (len == NULL || parts->first == parts->second) {
+ return NULL;
+ }
+ const auto & p = *parts->first++;
+ *len = p.length();
+ return p.data();
+ }
+ }
+
+ void
+ SendEmailFailed::ice_print(std::ostream & buf) const
+ {
+ buf << "Failed to send email: " << message;
+ }
+}
+
diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.h b/gentoobrowse-api/service/notifications/mailserverimpl.h
new file mode 100644
index 0000000..23b17c2
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/mailserverimpl.h
@@ -0,0 +1,20 @@
+#ifndef MAILSERVERIMPL_H
+#define MAILSERVERIMPL_H
+
+#include <notifications.h>
+#include <visibility.h>
+
+namespace Gentoo {
+ namespace Service {
+ class DLL_PUBLIC MailServer : public Gentoo::MailServer {
+ public:
+ void sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current &) override;
+
+ private:
+ static const char * writeBody(void ** buf, int * len, void * arg);
+ };
+ }
+}
+
+#endif
+
diff --git a/gentoobrowse-api/service/notifications/notifications.ice b/gentoobrowse-api/service/notifications/notifications.ice
new file mode 100644
index 0000000..a60fa28
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/notifications.ice
@@ -0,0 +1,49 @@
+#ifndef GENTOO_NOTIFICATIONS
+#define GENTOO_NOTIFICATIONS
+
+#include <user-models.ice>
+#include <portage-models.ice>
+
+module Gentoo {
+ class MimePart {
+ string mimetype;
+ string payload;
+ };
+
+ sequence<MimePart> MimeParts;
+
+ struct Address {
+ string name;
+ string address;
+ };
+
+ class Email {
+ Address to;
+ Address from;
+ string subject;
+ MimeParts body;
+ };
+
+ ["cpp:ice_print"]
+ exception SendEmailFailed {
+ string message;
+ };
+
+ struct NewsContent {
+ Categories categories;
+ Packages packages;
+ Ebuilds ebuilds;
+ };
+
+ interface Notifications {
+ Email getSignup(NewUser user);
+ Email getNews(User user, NewsContent nc);
+ };
+
+ interface MailServer {
+ idempotent void sendEmail(Email msg) throws SendEmailFailed;
+ };
+};
+
+#endif
+
diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.cpp b/gentoobrowse-api/service/notifications/notificationsimpl.cpp
new file mode 100644
index 0000000..d7dbee4
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/notificationsimpl.cpp
@@ -0,0 +1,59 @@
+#include "notificationsimpl.h"
+#include <libxml/parser.h>
+#include <base-xslt.h>
+#include <news-xslt.h>
+#include <signup-xslt.h>
+#include <string.h>
+#include <slicer/slicer.h>
+#include "xsltStreamSerializer.h"
+
+namespace Gentoo {
+ namespace Service {
+ xmlDocPtr xsltDocLoaderFunc (const xmlChar * URI, xmlDictPtr, int, void *, xsltLoadType)
+ {
+#define MATCH(name) \
+ if (xmlStrcmp(URI, BAD_CAST #name ".xslt") == 0) { \
+ return xmlParseMemory((char *)name ## _xslt, name ## _xslt_len); \
+ }
+ MATCH(base)
+ MATCH(news)
+ MATCH(signup)
+ return NULL;
+#undef MATCH
+ }
+
+ Notifications::Notifications()
+ {
+ xsltSetLoaderFunc(&xsltDocLoaderFunc);
+ news = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "news.xslt"), xsltFreeStylesheet);
+ signup = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "signup.xslt"), xsltFreeStylesheet);
+ xsltSetLoaderFunc(NULL);
+ }
+
+ Gentoo::EmailPtr Notifications::getSignup(const Gentoo::NewUserPtr & u, const Ice::Current &)
+ {
+ auto e = basicMail("Welcome", u);
+ Slicer::SerializeAny<XsltStreamSerializer>(u, e, signup.get());
+ return e;
+ }
+
+ Gentoo::EmailPtr Notifications::getNews(const Gentoo::UserPtr & u, const Gentoo::NewsContent & c, const Ice::Current &)
+ {
+ auto e = basicMail("Latest updates", u);
+ Slicer::SerializeAny<XsltStreamSerializer>(c, e, news.get());
+ return e;
+ }
+
+ Gentoo::EmailPtr Notifications::basicMail(const std::string & subject, Gentoo::UserPtr u)
+ {
+ Gentoo::EmailPtr e = new Gentoo::Email();
+ e->subject = "Gentoo Browse: " + subject;
+ e->from.name = "Gentoo Browse";
+ e->from.address = "noreply@gentoobrowse.randomdan.homeip.net";
+ e->to.name = u->userrealname;
+ e->to.address = u->useremail;
+ return e;
+ }
+ }
+}
+
diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.h b/gentoobrowse-api/service/notifications/notificationsimpl.h
new file mode 100644
index 0000000..1d3b1fc
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/notificationsimpl.h
@@ -0,0 +1,29 @@
+#ifndef NOTIFICATIONSIMPL_H
+#define NOTIFICATIONSIMPL_H
+
+#include <notifications.h>
+#include <visibility.h>
+#include <memory>
+#include <libxslt/documents.h>
+
+namespace Gentoo {
+ namespace Service {
+ class DLL_PUBLIC Notifications : public Gentoo::Notifications {
+ public:
+ Notifications();
+
+ Gentoo::EmailPtr getSignup(const Gentoo::NewUserPtr &, const Ice::Current &) override;
+ Gentoo::EmailPtr getNews(const Gentoo::UserPtr &, const Gentoo::NewsContent &, const Ice::Current &) override;
+
+ private:
+ Gentoo::EmailPtr basicMail(const std::string &, Gentoo::UserPtr u);
+
+ typedef std::shared_ptr<xsltStylesheet> xsltSSPtr;
+ xsltSSPtr news;
+ xsltSSPtr signup;
+ };
+ }
+}
+
+#endif
+
diff --git a/gentoobrowse-api/service/notifications/xslt/base.xslt b/gentoobrowse-api/service/notifications/xslt/base.xslt
new file mode 100644
index 0000000..9db7752
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/xslt/base.xslt
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:project2="http://project2.randomdan.homeip.net" exclude-result-prefixes="project2" >
+ <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes" />
+
+ <xsl:template match="/*">
+ <xsl:text disable-output-escaping="yes">&lt;!DOCTYPE html&gt;&#10;</xsl:text>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap.min.css" rel="stylesheet" media="screen" />
+ <link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen" />
+ <link href="http://gentoobrowse.randomdan.homeip.net/css/general.css" rel="stylesheet" media="screen" />
+ <title><xsl:call-template name="title" /></title>
+ </head>
+ <body data-spy="scroll" data-target=".bs-docs-sidebar">
+ <!-- nav -->
+ <nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="brand" href="http://gentoobrowse.randomdan.homeip.net/">Gentoo Browse</a>
+ </div>
+ </div>
+ </nav>
+
+ <!-- page content -->
+ <header class="jumbotron subhead">
+ <div class="container">
+ <h1>
+ <xsl:call-template name="title" />
+ </h1>
+ </div>
+ </header>
+ <div class="container">
+ <article class="row">
+ <xsl:call-template name="content" />
+ <section class="signoff">
+ </section>
+ </article>
+ </div>
+
+ <!-- footer -->
+ <footer class="footer">
+ <div class="container">
+ <p>Gentoo Browse is not an official Gentoo website. The name "Gentoo" and the "g" logo are trademarks of the Gentoo Foundation, Inc.</p>
+ </div>
+ </footer>
+
+ <script src="http://code.jquery.com/jquery-latest.js"></script>
+ <script src="http://gentoobrowse.randomdan.homeip.net/js/bootstrap.min.js"></script>
+ <script src="http://gentoobrowse.randomdan.homeip.net/js/js.js"></script>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template name="head">
+ </xsl:template>
+
+ <xsl:template name="header">
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/gentoobrowse-api/service/notifications/xslt/news.xslt b/gentoobrowse-api/service/notifications/xslt/news.xslt
new file mode 100644
index 0000000..6b2ea29
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/xslt/news.xslt
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://exslt.org/strings" xmlns:project2="http://project2.randomdan.homeip.net" version="1.0" exclude-result-prefixes="project2 fn">
+ <xsl:import href="base.xslt"/>
+ <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes"/>
+
+ <xsl:template name="title">Latest news</xsl:template>
+
+ <xsl:template name="content">
+ <xsl:variable name="packages" select="packages/package[packageid = ../../ebuilds/ebuild/packageid]"/>
+ <xsl:variable name="categories" select="categories/category[categoryid = $packages/categoryid]"/>
+ <section>
+ <h2 class="page-header">New versions of tracked packages</h2>
+ <div class="span3 bs-docs-sidebar">
+ <ul class="nav nav-list bs-docs-sidenav">
+ <xsl:for-each select="$categories">
+ <xsl:sort select="name" data-type="text" order="ascending"/>
+ <li>
+ <a>
+ <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="name" /></xsl:attribute>
+ <i class="icon-chevron-right"></i>
+ <xsl:value-of select="name" />
+ </a>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <div class="span9">
+ <xsl:for-each select="$categories">
+ <xsl:sort select="name" data-type="text" order="ascending"/>
+ <xsl:variable name="category" select="." />
+ <xsl:for-each select="$packages[categoryid = current()/categoryid]">
+ <xsl:sort select="name" data-type="text" order="ascending"/>
+ <xsl:variable name="package" select="." />
+ <p>
+ <a>
+ <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" /></xsl:attribute>
+ <xsl:value-of select="$category/name" />
+ </a>
+ /
+ <a>
+ <xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" />/<xsl:value-of select="name" /></xsl:attribute>
+ <xsl:value-of select="name" />
+ </a>
+ :
+ <xsl:value-of select="description" />
+ </p>
+ <xsl:for-each select="../../ebuilds/ebuild[packageid = current()/packageid]">
+ <p>
+ v<xsl:value-of select="version" />
+ </p>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:for-each>
+ </div>
+ </section>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/gentoobrowse-api/service/notifications/xslt/signup.xslt b/gentoobrowse-api/service/notifications/xslt/signup.xslt
new file mode 100644
index 0000000..e579835
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/xslt/signup.xslt
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:import href="base.xslt"/>
+
+ <xsl:template name="title">Welcome to Gentoo Browse</xsl:template>
+
+ <xsl:template name="content">
+ <section>
+ <h2 class="page-header">Confirmation</h2>
+ <p>
+ Click <a>
+ <xsl:attribute name="href">
+ <xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text>
+ <xsl:value-of select="verifyguid" />
+ </xsl:attribute>
+ <xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text>
+ <xsl:value-of select="verifyguid" />
+ </a> to confirm your account.
+ </p>
+ <p>Or alternatively, copy and paste this code into the confirmation page: <xsl:value-of select="verifyguid" /></p>
+ </section>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp
new file mode 100644
index 0000000..5a71b35
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp
@@ -0,0 +1,82 @@
+#include "xsltStreamSerializer.h"
+#include <libxslt/xsltInternals.h>
+#include <libxml/HTMLtree.h>
+#include <factory.impl.h>
+#include <processPipes.h>
+#include <sys/wait.h>
+
+namespace Gentoo {
+ static int xmlstrmclosecallback(void * context)
+ {
+ ((std::ostream*)context)->flush();
+ return 0;
+ }
+
+ static int xmlstrmwritecallback(void * context, const char * buffer, int len)
+ {
+ ((std::ostream*)context)->write(buffer, len);
+ return len;
+ }
+
+ XsltStreamSerializer::XsltStreamSerializer(Gentoo::EmailPtr e, xsltStylesheet * ss) :
+ Slicer::XmlDocumentSerializer(doc),
+ mail(e),
+ doc(nullptr),
+ stylesheet(ss)
+ {
+ }
+
+ XsltStreamSerializer::~XsltStreamSerializer()
+ {
+ delete doc;
+ }
+
+ void
+ XsltStreamSerializer::Serialize(Slicer::ModelPartForRootPtr mp)
+ {
+ Slicer::XmlDocumentSerializer::Serialize(mp);
+ auto result = std::shared_ptr<xmlDoc>(xsltApplyStylesheet(stylesheet, doc->cobj(), nullptr), xmlFreeDoc);
+ if (!result) {
+ throw xmlpp::exception("Failed to apply XSL transform");
+ }
+ appendText(result.get());
+ appendHtml(result.get());
+ }
+
+ void XsltStreamSerializer::appendHtml(xmlDoc * result) const
+ {
+ std::stringstream strm;
+ xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, NULL);
+ htmlDocContentDumpFormatOutput(buf, result, "utf-8", 0);
+ xmlOutputBufferClose(buf);
+ mail->body.push_back(new Gentoo::MimePart("text/html", strm.str()));
+ }
+
+ void XsltStreamSerializer::appendText(xmlDoc * result) const
+ {
+ std::stringstream strm;
+ std::vector<std::string> callLynx;
+ callLynx.push_back("/usr/bin/lynx");
+ callLynx.push_back("-dump");
+ callLynx.push_back("-stdin");
+ std::string widthArg = "-width=105";
+ callLynx.push_back(widthArg);
+ AdHoc::System::ProcessPipes fds(callLynx, true, true, false);
+ FILE * lynxIn = fdopen(fds.fdIn(), "w");
+ // Fixed encoding as we want the result to go back into a ustring
+ htmlNodeDumpFileFormat(lynxIn, result, xmlDocGetRootElement(result), "utf-8", 0);
+ fclose(lynxIn);
+ char buf[1024];
+ int r;
+ while ((r = read(fds.fdOut(), buf, sizeof(buf))) > 0) {
+ strm.write(buf, r);
+ }
+ int status;
+ waitpid(fds.pid(), &status, 0);
+ if (status != 0) {
+ throw std::runtime_error("Lynx failed");
+ }
+ mail->body.push_back(new Gentoo::MimePart("text/plain", strm.str()));
+ }
+}
+
diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.h b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h
new file mode 100644
index 0000000..492b954
--- /dev/null
+++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h
@@ -0,0 +1,28 @@
+#ifndef ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H
+#define ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H
+
+#include <slicer/xml/serializer.h>
+#include <visibility.h>
+#include <libxslt/transform.h>
+#include <notifications.h>
+
+namespace Gentoo {
+ class DLL_PUBLIC XsltStreamSerializer : public Slicer::XmlDocumentSerializer {
+ public:
+ XsltStreamSerializer(Gentoo::EmailPtr, xsltStylesheet *);
+ ~XsltStreamSerializer();
+
+ void Serialize(Slicer::ModelPartForRootPtr mp) override;
+
+ protected:
+ void appendHtml(xmlDoc *) const;
+ void appendText(xmlDoc *) const;
+
+ Gentoo::EmailPtr mail;
+ xmlpp::Document * doc;
+ xsltStylesheet * stylesheet;
+ };
+}
+
+#endif
+