summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-12-02 07:26:33 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-12-02 07:26:33 +0200
commitcface45b7cfd084f65ead1d5bfa6ecdc639d7017 (patch)
tree0c2b7f900ee3f116474f9789173b60423b98cf4f /src
parent5b147a3976fcf007a75e9b16cc0cea96011f2e9b (diff)
Added MimeHooks
This is a very powerful mechanism: translate the contents of any request to something else when the original MIME type matches a configured regexp. The external hook command may still elect not to process the request.
Diffstat (limited to 'src')
-rw-r--r--src/app.c10
-rw-r--r--src/app.h2
-rw-r--r--src/gmrequest.c55
-rw-r--r--src/mimehooks.c139
-rw-r--r--src/mimehooks.h31
5 files changed, 222 insertions, 15 deletions
diff --git a/src/app.c b/src/app.c
index 610403f0..b799b627 100644
--- a/src/app.c
+++ b/src/app.c
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include "defs.h" 25#include "defs.h"
26#include "embedded.h" 26#include "embedded.h"
27#include "feeds.h" 27#include "feeds.h"
28#include "mimehooks.h"
28#include "gmcerts.h" 29#include "gmcerts.h"
29#include "gmdocument.h" 30#include "gmdocument.h"
30#include "gmutil.h" 31#include "gmutil.h"
@@ -91,6 +92,7 @@ static const char *downloadDir_App_ = "~/Downloads";
91struct Impl_App { 92struct Impl_App {
92 iCommandLine args; 93 iCommandLine args;
93 iString * execPath; 94 iString * execPath;
95 iMimeHooks * mimehooks;
94 iGmCerts * certs; 96 iGmCerts * certs;
95 iVisited * visited; 97 iVisited * visited;
96 iBookmarks * bookmarks; 98 iBookmarks * bookmarks;
@@ -342,6 +344,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
342 d->running = iFalse; 344 d->running = iFalse;
343 d->window = NULL; 345 d->window = NULL;
344 set_Atomic(&d->pendingRefresh, iFalse); 346 set_Atomic(&d->pendingRefresh, iFalse);
347 d->mimehooks = new_MimeHooks();
345 d->certs = new_GmCerts(dataDir_App_); 348 d->certs = new_GmCerts(dataDir_App_);
346 d->visited = new_Visited(); 349 d->visited = new_Visited();
347 d->bookmarks = new_Bookmarks(); 350 d->bookmarks = new_Bookmarks();
@@ -355,6 +358,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
355 load_Keys(dataDir_App_); 358 load_Keys(dataDir_App_);
356 load_Visited(d->visited, dataDir_App_); 359 load_Visited(d->visited, dataDir_App_);
357 load_Bookmarks(d->bookmarks, dataDir_App_); 360 load_Bookmarks(d->bookmarks, dataDir_App_);
361 load_MimeHooks(d->mimehooks, dataDir_App_);
358 if (isFirstRun) { 362 if (isFirstRun) {
359 /* Create the default bookmarks for a quick start. */ 363 /* Create the default bookmarks for a quick start. */
360 add_Bookmarks(d->bookmarks, 364 add_Bookmarks(d->bookmarks,
@@ -433,6 +437,8 @@ static void deinit_App(iApp *d) {
433 save_Visited(d->visited, dataDir_App_); 437 save_Visited(d->visited, dataDir_App_);
434 delete_Visited(d->visited); 438 delete_Visited(d->visited);
435 delete_GmCerts(d->certs); 439 delete_GmCerts(d->certs);
440 save_MimeHooks(d->mimehooks);
441 delete_MimeHooks(d->mimehooks);
436 deinit_SortedArray(&d->tickers); 442 deinit_SortedArray(&d->tickers);
437 delete_Window(d->window); 443 delete_Window(d->window);
438 d->window = NULL; 444 d->window = NULL;
@@ -709,6 +715,10 @@ void removeTicker_App(iTickerFunc ticker, iAny *context) {
709 remove_SortedArray(&d->tickers, &(iTicker){ context, ticker }); 715 remove_SortedArray(&d->tickers, &(iTicker){ context, ticker });
710} 716}
711 717
718iMimeHooks *mimeHooks_App(void) {
719 return app_.mimehooks;
720}
721
712iGmCerts *certs_App(void) { 722iGmCerts *certs_App(void) {
713 return app_.certs; 723 return app_.certs;
714} 724}
diff --git a/src/app.h b/src/app.h
index 05341523..bc086dfe 100644
--- a/src/app.h
+++ b/src/app.h
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34iDeclareType(Bookmarks) 34iDeclareType(Bookmarks)
35iDeclareType(DocumentWidget) 35iDeclareType(DocumentWidget)
36iDeclareType(GmCerts) 36iDeclareType(GmCerts)
37iDeclareType(MimeHooks)
37iDeclareType(Visited) 38iDeclareType(Visited)
38iDeclareType(Window) 39iDeclareType(Window)
39 40
@@ -65,6 +66,7 @@ enum iColorTheme colorTheme_App (void);
65const iString * schemeProxy_App (iRangecc scheme); 66const iString * schemeProxy_App (iRangecc scheme);
66iBool willUseProxy_App (const iRangecc scheme); 67iBool willUseProxy_App (const iRangecc scheme);
67 68
69iMimeHooks * mimeHooks_App (void);
68iGmCerts * certs_App (void); 70iGmCerts * certs_App (void);
69iVisited * visited_App (void); 71iVisited * visited_App (void);
70iBookmarks * bookmarks_App (void); 72iBookmarks * bookmarks_App (void);
diff --git a/src/gmrequest.c b/src/gmrequest.c
index b51d3b1f..572e6a5c 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include "gmcerts.h" 25#include "gmcerts.h"
26#include "gopher.h" 26#include "gopher.h"
27#include "app.h" /* dataDir_App() */ 27#include "app.h" /* dataDir_App() */
28#include "mimehooks.h"
28#include "feeds.h" 29#include "feeds.h"
29#include "ui/text.h" 30#include "ui/text.h"
30#include "embedded.h" 31#include "embedded.h"
@@ -131,7 +132,8 @@ struct Impl_GmRequest {
131 iTlsRequest * req; 132 iTlsRequest * req;
132 iGopher gopher; 133 iGopher gopher;
133 iGmResponse * resp; 134 iGmResponse * resp;
134 iBool respLocked; 135 iBool isRespLocked;
136 iBool isRespFiltered;
135 iAtomicInt allowUpdate; 137 iAtomicInt allowUpdate;
136 iAudience * updated; 138 iAudience * updated;
137 iAudience * finished; 139 iAudience * finished;
@@ -164,13 +166,10 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
164 } 166 }
165} 167}
166 168
167static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { 169static int processIncomingData_GmRequest_(iGmRequest *d, const iBlock *data) {
168 iBool notifyUpdate = iFalse; 170 iBool notifyUpdate = iFalse;
169 iBool notifyDone = iFalse; 171 iBool notifyDone = iFalse;
170 lock_Mutex(d->mtx); 172 iGmResponse *resp = d->resp;
171 iGmResponse *resp =d->resp;
172 iAssert(d->state != finished_GmRequestState); /* notifications out of order? */
173 iBlock *data = readAll_TlsRequest(req);
174 if (d->state == receivingHeader_GmRequestState) { 173 if (d->state == receivingHeader_GmRequestState) {
175 appendCStrN_String(&resp->meta, constData_Block(data), size_Block(data)); 174 appendCStrN_String(&resp->meta, constData_Block(data), size_Block(data));
176 /* Check if the header line is complete. */ 175 /* Check if the header line is complete. */
@@ -208,6 +207,9 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) {
208 resp->statusCode = code; 207 resp->statusCode = code;
209 d->state = receivingBody_GmRequestState; 208 d->state = receivingBody_GmRequestState;
210 notifyUpdate = iTrue; 209 notifyUpdate = iTrue;
210 if (willTryFilter_MimeHooks(mimeHooks_App(), &resp->meta)) {
211 d->isRespFiltered = iTrue;
212 }
211 } 213 }
212 checkServerCertificate_GmRequest_(d); 214 checkServerCertificate_GmRequest_(d);
213 iRelease(metaPattern); 215 iRelease(metaPattern);
@@ -217,10 +219,21 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) {
217 append_Block(&resp->body, data); 219 append_Block(&resp->body, data);
218 notifyUpdate = iTrue; 220 notifyUpdate = iTrue;
219 } 221 }
222 return (notifyUpdate ? 1 : 0) | (notifyDone ? 2 : 0);
223}
224
225static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) {
226 lock_Mutex(d->mtx);
227 iGmResponse *resp = d->resp;
228 iAssert(d->state != finished_GmRequestState); /* notifications out of order? */
229 iBlock * data = readAll_TlsRequest(req);
230 const int ubits = processIncomingData_GmRequest_(d, data);
231 iBool notifyUpdate = (ubits & 1) != 0;
232 iBool notifyDone = (ubits & 2) != 0;
220 initCurrent_Time(&resp->when); 233 initCurrent_Time(&resp->when);
221 delete_Block(data); 234 delete_Block(data);
222 unlock_Mutex(d->mtx); 235 unlock_Mutex(d->mtx);
223 if (notifyUpdate) { 236 if (notifyUpdate && !d->isRespFiltered) {
224 const iBool allowed = exchange_Atomic(&d->allowUpdate, iFalse); 237 const iBool allowed = exchange_Atomic(&d->allowUpdate, iFalse);
225 if (allowed) { 238 if (allowed) {
226 iNotifyAudience(d, updated, GmRequestUpdated); 239 iNotifyAudience(d, updated, GmRequestUpdated);
@@ -247,7 +260,18 @@ static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) {
247 set_String(&d->resp->meta, errorMessage_TlsRequest(req)); 260 set_String(&d->resp->meta, errorMessage_TlsRequest(req));
248 } 261 }
249 checkServerCertificate_GmRequest_(d); 262 checkServerCertificate_GmRequest_(d);
250 unlock_Mutex(d->mtx); 263 /* Check for mimehooks. */
264 if (d->isRespFiltered && d->state == finished_GmRequestState) {
265 iBlock *xbody = tryFilter_MimeHooks(mimeHooks_App(), &d->resp->meta, &d->resp->body);
266 if (xbody) {
267 clear_String(&d->resp->meta);
268 clear_Block(&d->resp->body);
269 d->state = receivingHeader_GmRequestState;
270 processIncomingData_GmRequest_(d, xbody);
271 d->state = finished_GmRequestState;
272 }
273 }
274 unlock_Mutex(d->mtx);
251 iNotifyAudience(d, finished, GmRequestFinished); 275 iNotifyAudience(d, finished, GmRequestFinished);
252} 276}
253 277
@@ -425,7 +449,8 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host,
425void init_GmRequest(iGmRequest *d, iGmCerts *certs) { 449void init_GmRequest(iGmRequest *d, iGmCerts *certs) {
426 d->mtx = new_Mutex(); 450 d->mtx = new_Mutex();
427 d->resp = new_GmResponse(); 451 d->resp = new_GmResponse();
428 d->respLocked = iFalse; 452 d->isRespLocked = iFalse;
453 d->isRespFiltered = iFalse;
429 set_Atomic(&d->allowUpdate, iTrue); 454 set_Atomic(&d->allowUpdate, iTrue);
430 init_String(&d->url); 455 init_String(&d->url);
431 init_Gopher(&d->gopher); 456 init_Gopher(&d->gopher);
@@ -623,16 +648,16 @@ void cancel_GmRequest(iGmRequest *d) {
623} 648}
624 649
625iGmResponse *lockResponse_GmRequest(iGmRequest *d) { 650iGmResponse *lockResponse_GmRequest(iGmRequest *d) {
626 iAssert(!d->respLocked); 651 iAssert(!d->isRespLocked);
627 lock_Mutex(d->mtx); 652 lock_Mutex(d->mtx);
628 d->respLocked = iTrue; 653 d->isRespLocked = iTrue;
629 return d->resp; 654 return d->resp;
630} 655}
631 656
632void unlockResponse_GmRequest(iGmRequest *d) { 657void unlockResponse_GmRequest(iGmRequest *d) {
633 if (d) { 658 if (d) {
634 iAssert(d->respLocked); 659 iAssert(d->isRespLocked);
635 d->respLocked = iFalse; 660 d->isRespLocked = iFalse;
636 set_Atomic(&d->allowUpdate, iTrue); 661 set_Atomic(&d->allowUpdate, iTrue);
637 unlock_Mutex(d->mtx); 662 unlock_Mutex(d->mtx);
638 } 663 }
diff --git a/src/mimehooks.c b/src/mimehooks.c
new file mode 100644
index 00000000..3d999aba
--- /dev/null
+++ b/src/mimehooks.c
@@ -0,0 +1,139 @@
1#include "mimehooks.h"
2
3#include <the_Foundation/file.h>
4#include <the_Foundation/path.h>
5#include <the_Foundation/process.h>
6#include <the_Foundation/stringlist.h>
7
8iDefineTypeConstruction(FilterHook)
9
10void init_FilterHook(iFilterHook *d) {
11 init_String(&d->label);
12 init_String(&d->mimePattern);
13 init_String(&d->command);
14 d->mimeRegex = NULL;
15}
16
17void deinit_FilterHook(iFilterHook *d) {
18 iRelease(d->mimeRegex);
19 deinit_String(&d->command);
20 deinit_String(&d->mimePattern);
21 deinit_String(&d->label);
22}
23
24void setMimePattern_FilterHook(iFilterHook *d, const iString *pattern) {
25 iReleasePtr(&d->mimeRegex);
26 d->mimeRegex = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption);
27}
28
29void setCommand_FilterHook(iFilterHook *d, const iString *command) {
30 set_String(&d->command, command);
31}
32
33iBlock *run_FilterHook_(const iFilterHook *d, const iString *mime, const iBlock *body) {
34 iProcess * proc = new_Process();
35 iStringList *args = new_StringList();
36 iRangecc seg = iNullRange;
37 while (nextSplit_Rangecc(range_String(&d->command), ";", &seg)) {
38 pushBackRange_StringList(args, seg);
39 }
40 seg = iNullRange;
41 while (nextSplit_Rangecc(range_String(mime), ";", &seg)) {
42 pushBackRange_StringList(args, seg);
43 }
44 setArguments_Process(proc, args);
45 iRelease(args);
46 start_Process(proc);
47 writeInput_Process(proc, body);
48 waitForFinished_Process(proc);
49 iBlock *output = readOutput_Process(proc);
50 if (!startsWith_Rangecc(range_Block(output), "20")) {
51 /* Didn't produce valid output. */
52 delete_Block(output);
53 output = NULL;
54 }
55 iRelease(proc);
56 return output;
57}
58
59/*----------------------------------------------------------------------------------------------*/
60
61struct Impl_MimeHooks {
62 iPtrArray filters;
63};
64
65iDefineTypeConstruction(MimeHooks)
66
67void init_MimeHooks(iMimeHooks *d) {
68 init_PtrArray(&d->filters);
69}
70
71void deinit_MimeHooks(iMimeHooks *d) {
72 iForEach(PtrArray, i, &d->filters) {
73 delete_FilterHook(i.ptr);
74 }
75 deinit_PtrArray(&d->filters);
76}
77
78iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) {
79 /* TODO: Combine this function with tryFilter_MimeHooks? */
80 iRegExpMatch m;
81 iConstForEach(PtrArray, i, &d->filters) {
82 const iFilterHook *xc = i.ptr;
83 init_RegExpMatch(&m);
84 if (matchString_RegExp(xc->mimeRegex, mime, &m)) {
85 return iTrue;
86 }
87 }
88 return iFalse;
89}
90
91iBlock *tryFilter_MimeHooks(const iMimeHooks *d, const iString *mime, const iBlock *body) {
92 iRegExpMatch m;
93 iConstForEach(PtrArray, i, &d->filters) {
94 const iFilterHook *xc = i.ptr;
95 init_RegExpMatch(&m);
96 if (matchString_RegExp(xc->mimeRegex, mime, &m)) {
97 iBlock *result = run_FilterHook_(xc, mime, body);
98 if (result) {
99 return result;
100 }
101 }
102 }
103 return NULL;
104}
105
106static const char *mimeHooksFilename_MimeHooks_ = "mimehooks.txt";
107
108void load_MimeHooks(iMimeHooks *d, const char *saveDir) {
109 iFile *f = newCStr_File(concatPath_CStr(saveDir, mimeHooksFilename_MimeHooks_));
110 if (open_File(f, read_FileMode | text_FileMode)) {
111 iBlock * src = readAll_File(f);
112 iRangecc srcLine = iNullRange;
113 int pos = 0;
114 iRangecc lines[3];
115 iZap(lines);
116 while (nextSplit_Rangecc(range_Block(src), "\n", &srcLine)) {
117 iRangecc line = srcLine;
118 trim_Rangecc(&line);
119 if (isEmpty_Range(&line)) {
120 continue;
121 }
122 lines[pos++] = line;
123 if (pos == 3) {
124 iFilterHook *hook = new_FilterHook();
125 setRange_String(&hook->label, lines[0]);
126 setMimePattern_FilterHook(hook, collect_String(newRange_String(lines[1])));
127 setCommand_FilterHook(hook, collect_String(newRange_String(lines[2])));
128 pushBack_PtrArray(&d->filters, hook);
129 }
130 }
131 delete_Block(src);
132 }
133 iRelease(f);
134}
135
136void save_MimeHooks(const iMimeHooks *d) {
137 iUnused(d);
138}
139
diff --git a/src/mimehooks.h b/src/mimehooks.h
new file mode 100644
index 00000000..c78a3c86
--- /dev/null
+++ b/src/mimehooks.h
@@ -0,0 +1,31 @@
1#pragma once
2
3#include <the_Foundation/audience.h>
4#include <the_Foundation/ptrarray.h>
5#include <the_Foundation/regexp.h>
6#include <the_Foundation/string.h>
7
8iDeclareType(FilterHook)
9iDeclareTypeConstruction(FilterHook)
10
11struct Impl_FilterHook {
12 iString label;
13 iString mimePattern;
14 iRegExp *mimeRegex;
15 iString command;
16};
17
18void setMimePattern_FilterHook (iFilterHook *, const iString *pattern);
19void setCommand_FilterHook (iFilterHook *, const iString *command);
20
21/*----------------------------------------------------------------------------------------------*/
22
23iDeclareType(MimeHooks)
24iDeclareTypeConstruction(MimeHooks)
25
26iBool willTryFilter_MimeHooks (const iMimeHooks *, const iString *mime);
27iBlock * tryFilter_MimeHooks (const iMimeHooks *, const iString *mime,
28 const iBlock *body);
29
30void load_MimeHooks (iMimeHooks *, const char *saveDir);
31void save_MimeHooks (const iMimeHooks *);