summaryrefslogtreecommitdiff
path: root/src/mimehooks.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-17 16:57:49 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-17 16:57:49 +0200
commitc6a3afccfc5ef679a82275c5f045336c2d48f643 (patch)
tree1f026eed5c92a84d45efc2bc68830ca7b67bae73 /src/mimehooks.c
parent42972fa58f2589b6891db6a310d8f0454921e717 (diff)
Added a built-in Atom-to-Gemini feed translator
If no user-configured MIME hooks handle an Atom XML document, it will be translated using a built-in filter hook. Only Atom is supported. IssueID #78
Diffstat (limited to 'src/mimehooks.c')
-rw-r--r--src/mimehooks.c119
1 files changed, 118 insertions, 1 deletions
diff --git a/src/mimehooks.c b/src/mimehooks.c
index f4ec6bf4..b2110cfb 100644
--- a/src/mimehooks.c
+++ b/src/mimehooks.c
@@ -6,6 +6,7 @@
6#include <the_Foundation/path.h> 6#include <the_Foundation/path.h>
7#include <the_Foundation/process.h> 7#include <the_Foundation/process.h>
8#include <the_Foundation/stringlist.h> 8#include <the_Foundation/stringlist.h>
9#include <the_Foundation/xml.h>
9 10
10iDefineTypeConstruction(FilterHook) 11iDefineTypeConstruction(FilterHook)
11 12
@@ -69,6 +70,109 @@ iBlock *run_FilterHook_(const iFilterHook *d, const iString *mime, const iBlock
69 70
70/*----------------------------------------------------------------------------------------------*/ 71/*----------------------------------------------------------------------------------------------*/
71 72
73static iRegExp *xmlMimePattern_(void) {
74 static iRegExp *xmlMime_;
75 if (!xmlMime_) {
76 xmlMime_ = new_RegExp("(application|text)/(atom\\+)?xml", caseInsensitive_RegExpOption);
77 }
78 return xmlMime_;
79}
80
81static iBlock *translateAtomXmlToGeminiFeed_(const iString *mime, const iBlock *source,
82 const iString *requestUrl) {
83 iRegExpMatch m;
84 init_RegExpMatch(&m);
85 if (!matchString_RegExp(xmlMimePattern_(), mime, &m)) {
86 return NULL;
87 }
88 iBlock * output = NULL;
89 iXmlDocument *doc = new_XmlDocument();
90 iString src;
91 initBlock_String(&src, source); /* assume it's UTF-8 */
92 if (!parse_XmlDocument(doc, &src)) {
93 goto finished;
94 }
95 const iXmlElement *feed = &doc->root;
96 if (!equal_Rangecc(feed->name, "feed")) {
97 goto finished;
98 }
99 if (!equal_Rangecc(attribute_XmlElement(feed, "xmlns"), "http://www.w3.org/2005/Atom")) {
100 goto finished;
101 }
102 iString *title = collect_String(decodedContent_XmlElement(child_XmlElement(feed, "title")));
103 if (isEmpty_String(title)) {
104 goto finished;
105 }
106 iString *subtitle = collect_String(decodedContent_XmlElement(child_XmlElement(feed, "subtitle")));
107 iString out;
108 init_String(&out);
109 format_String(&out,
110 "20 text/gemini\r\n"
111 "# %s\n\n", cstr_String(title));
112 if (!isEmpty_String(subtitle)) {
113 appendFormat_String(&out, "## %s\n\n", cstr_String(subtitle));
114 }
115 iRegExp *datePattern =
116 iClob(new_RegExp("^([0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9])T.*", caseSensitive_RegExpOption));
117 iBeginCollect();
118 iConstForEach(PtrArray, i, &feed->children) {
119 iEndCollect();
120 iBeginCollect();
121 const iXmlElement *entry = i.ptr;
122 if (!equal_Rangecc(entry->name, "entry")) {
123 continue;
124 }
125 title = collect_String(decodedContent_XmlElement(child_XmlElement(entry, "title")));
126 if (isEmpty_String(title)) {
127 continue;
128 }
129 const iString *updated =
130 collect_String(decodedContent_XmlElement(child_XmlElement(entry, "updated")));
131 iRegExpMatch m;
132 init_RegExpMatch(&m);
133 if (!matchString_RegExp(datePattern, updated, &m)) {
134 continue;
135 }
136 iRangecc url = iNullRange;
137 iConstForEach(PtrArray, j, &entry->children) {
138 const iXmlElement *link = j.ptr;
139 if (!equal_Rangecc(link->name, "link")) {
140 continue;
141 }
142 const iRangecc href = attribute_XmlElement(link, "href");
143 const iRangecc rel = attribute_XmlElement(link, "rel");
144 const iRangecc type = attribute_XmlElement(link, "type");
145 if (startsWithCase_Rangecc(href, "gemini:")) {
146 url = href;
147 /* We're happy with the first gemini URL. */
148 /* TODO: Are we? */
149 break;
150 }
151 url = href;
152 iUnused(rel, type);
153 }
154 if (isEmpty_Range(&url)) {
155 continue;
156 }
157 appendFormat_String(&out, "=> %s %s - %s\n",
158 cstr_Rangecc(url),
159 cstr_Rangecc(capturedRange_RegExpMatch(&m, 1)),
160 cstr_String(title));
161 }
162 iEndCollect();
163 appendCStr_String(&out,
164 "This Atom XML document has been automatically translated to a Gemini feed "
165 "to allow subscribing to it.\n");
166 output = copy_Block(utf8_String(&out));
167 deinit_String(&out);
168finished:
169 delete_XmlDocument(doc);
170 deinit_String(&src);
171 return output;
172}
173
174/*----------------------------------------------------------------------------------------------*/
175
72struct Impl_MimeHooks { 176struct Impl_MimeHooks {
73 iPtrArray filters; 177 iPtrArray filters;
74}; 178};
@@ -87,7 +191,7 @@ void deinit_MimeHooks(iMimeHooks *d) {
87} 191}
88 192
89iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) { 193iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) {
90 /* TODO: Combine this function with tryFilter_MimeHooks? */ 194 /* TODO: Combine this function with tryFilter_MimeHooks! */
91 iRegExpMatch m; 195 iRegExpMatch m;
92 iConstForEach(PtrArray, i, &d->filters) { 196 iConstForEach(PtrArray, i, &d->filters) {
93 const iFilterHook *xc = i.ptr; 197 const iFilterHook *xc = i.ptr;
@@ -96,6 +200,11 @@ iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) {
96 return iTrue; 200 return iTrue;
97 } 201 }
98 } 202 }
203 /* Built-in filters. */
204 init_RegExpMatch(&m);
205 if (matchString_RegExp(xmlMimePattern_(), mime, &m)) {
206 return iTrue;
207 }
99 return iFalse; 208 return iFalse;
100} 209}
101 210
@@ -112,6 +221,14 @@ iBlock *tryFilter_MimeHooks(const iMimeHooks *d, const iString *mime, const iBlo
112 } 221 }
113 } 222 }
114 } 223 }
224 /* Built-in filters. */
225 init_RegExpMatch(&m);
226 if (matchString_RegExp(xmlMimePattern_(), mime, &m)) {
227 iBlock *result = translateAtomXmlToGeminiFeed_(mime, body, requestUrl);
228 if (result) {
229 return result;
230 }
231 }
115 return NULL; 232 return NULL;
116} 233}
117 234