diff options
-rw-r--r-- | src/gmrequest.c | 8 | ||||
-rw-r--r-- | src/mimehooks.c | 119 |
2 files changed, 123 insertions, 4 deletions
diff --git a/src/gmrequest.c b/src/gmrequest.c index 8626403f..9cdd627e 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -133,6 +133,7 @@ struct Impl_GmRequest { | |||
133 | iTlsRequest * req; | 133 | iTlsRequest * req; |
134 | iGopher gopher; | 134 | iGopher gopher; |
135 | iGmResponse * resp; | 135 | iGmResponse * resp; |
136 | iBool isFilterEnabled; | ||
136 | iBool isRespLocked; | 137 | iBool isRespLocked; |
137 | iBool isRespFiltered; | 138 | iBool isRespFiltered; |
138 | iAtomicInt allowUpdate; | 139 | iAtomicInt allowUpdate; |
@@ -208,7 +209,7 @@ static int processIncomingData_GmRequest_(iGmRequest *d, const iBlock *data) { | |||
208 | resp->statusCode = code; | 209 | resp->statusCode = code; |
209 | d->state = receivingBody_GmRequestState; | 210 | d->state = receivingBody_GmRequestState; |
210 | notifyUpdate = iTrue; | 211 | notifyUpdate = iTrue; |
211 | if (willTryFilter_MimeHooks(mimeHooks_App(), &resp->meta)) { | 212 | if (d->isFilterEnabled && willTryFilter_MimeHooks(mimeHooks_App(), &resp->meta)) { |
212 | d->isRespFiltered = iTrue; | 213 | d->isRespFiltered = iTrue; |
213 | } | 214 | } |
214 | } | 215 | } |
@@ -457,8 +458,9 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
457 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { | 458 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { |
458 | d->mtx = new_Mutex(); | 459 | d->mtx = new_Mutex(); |
459 | d->resp = new_GmResponse(); | 460 | d->resp = new_GmResponse(); |
460 | d->isRespLocked = iFalse; | 461 | d->isFilterEnabled = iTrue; |
461 | d->isRespFiltered = iFalse; | 462 | d->isRespLocked = iFalse; |
463 | d->isRespFiltered = iFalse; | ||
462 | set_Atomic(&d->allowUpdate, iTrue); | 464 | set_Atomic(&d->allowUpdate, iTrue); |
463 | init_String(&d->url); | 465 | init_String(&d->url); |
464 | init_Gopher(&d->gopher); | 466 | init_Gopher(&d->gopher); |
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 | ||
10 | iDefineTypeConstruction(FilterHook) | 11 | iDefineTypeConstruction(FilterHook) |
11 | 12 | ||
@@ -69,6 +70,109 @@ iBlock *run_FilterHook_(const iFilterHook *d, const iString *mime, const iBlock | |||
69 | 70 | ||
70 | /*----------------------------------------------------------------------------------------------*/ | 71 | /*----------------------------------------------------------------------------------------------*/ |
71 | 72 | ||
73 | static 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 | |||
81 | static 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); | ||
168 | finished: | ||
169 | delete_XmlDocument(doc); | ||
170 | deinit_String(&src); | ||
171 | return output; | ||
172 | } | ||
173 | |||
174 | /*----------------------------------------------------------------------------------------------*/ | ||
175 | |||
72 | struct Impl_MimeHooks { | 176 | struct Impl_MimeHooks { |
73 | iPtrArray filters; | 177 | iPtrArray filters; |
74 | }; | 178 | }; |
@@ -87,7 +191,7 @@ void deinit_MimeHooks(iMimeHooks *d) { | |||
87 | } | 191 | } |
88 | 192 | ||
89 | iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) { | 193 | iBool 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 | ||