summaryrefslogtreecommitdiff
path: root/gentoobrowse-api/service/xsltStreamSerializer.cpp
blob: acb5750991cf0a36a6beaa49e09bf6e3c5d94cbb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "xsltStreamSerializer.h"
#include "xml/serializer.h"
#include <array>
#include <cstdio>
#include <libxml/HTMLtree.h> // IWYU pragma: keep
#include <libxslt/transform.h>
#include <libxslt/xsltInternals.h>
#include <memory>
#include <mimeImpl.h>
#include <notifications/css/style.h>
#include <ostream>
#include <processPipes.h>
#include <stdexcept>
#include <string>
#include <sys/wait.h>
#include <unistd.h>
#include <utility>
#include <vector>

namespace Gentoo {
	using namespace IceTray::Mime;
	static const std::string css(style_css, style_css + style_css_len);

	static int
	xmlstrmclosecallback(void * context)
	{
		(static_cast<std::ostream *>(context))->flush();
		return 0;
	}

	static int
	xmlstrmwritecallback(void * context, const char * buffer, int len)
	{
		(static_cast<std::ostream *>(context))->write(buffer, len);
		return len;
	}

	XsltStreamSerializer::XsltStreamSerializer(IceTray::Mail::EmailPtr e, xsltStylesheet * ss) :
		mail(std::move(e)), stylesheet(ss)
	{
	}

	void
	XsltStreamSerializer::Serialize(Slicer::ModelPartForRootPtr mp)
	{
		Slicer::XmlDocumentSerializer::Serialize(mp);
		auto result = std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)>(
				xsltApplyStylesheet(stylesheet, doc.cobj(), nullptr), xmlFreeDoc);
		if (!result) {
			throw xmlpp::exception("Failed to apply XSL transform");
		}
		mail->content = std::make_shared<MultiPart>(
				Headers {}, "alternative", Parts {getText(result.get()), getHtml(result.get())});
	}

	IceTray::Mime::BasicPartPtr
	XsltStreamSerializer::getHtml(xmlDoc * result)
	{
		std::stringstream strm;
		xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, nullptr);
		htmlDocContentDumpFormatOutput(buf, result, "utf-8", 0);
		xmlOutputBufferClose(buf);
		return std::make_shared<MultiPart>(Headers {}, "related",
				Parts {
						std::make_shared<TextPart>(Headers {}, "text/html", strm.str()),
						std::make_shared<TextPart>(
								Headers {
										{"Content-Id", "<style.css@gentoobrowse.randomdan.homeip.net>"},
								},
								"text/css", css),
				});
	}

	IceTray::Mime::BasicPartPtr
	XsltStreamSerializer::getText(xmlDoc * result)
	{
		std::stringstream strm;
		std::vector<std::string> callLynx {
				"/usr/bin/lynx",
				"-dump",
				"-stdin",
				"-width=78",
		};
		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);
		std::array<char, BUFSIZ> buf {};
		ssize_t r;
		while ((r = read(fds.fdOut(), buf.data(), buf.size())) > 0) {
			strm.write(buf.data(), r);
		}
		int status;
		waitpid(fds.pid(), &status, 0);
		if (status != 0) {
			throw std::runtime_error("Lynx failed");
		}
		return std::make_shared<TextPart>(Headers {}, "text/plain", strm.str());
	}
}