From cface45b7cfd084f65ead1d5bfa6ecdc639d7017 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 2 Dec 2020 07:26:33 +0200 Subject: 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. --- src/app.c | 10 ++++ src/app.h | 2 + src/gmrequest.c | 55 ++++++++++++++++------ src/mimehooks.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mimehooks.h | 31 +++++++++++++ 5 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 src/mimehooks.c create mode 100644 src/mimehooks.h (limited to 'src') 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. */ #include "defs.h" #include "embedded.h" #include "feeds.h" +#include "mimehooks.h" #include "gmcerts.h" #include "gmdocument.h" #include "gmutil.h" @@ -91,6 +92,7 @@ static const char *downloadDir_App_ = "~/Downloads"; struct Impl_App { iCommandLine args; iString * execPath; + iMimeHooks * mimehooks; iGmCerts * certs; iVisited * visited; iBookmarks * bookmarks; @@ -342,6 +344,7 @@ static void init_App_(iApp *d, int argc, char **argv) { d->running = iFalse; d->window = NULL; set_Atomic(&d->pendingRefresh, iFalse); + d->mimehooks = new_MimeHooks(); d->certs = new_GmCerts(dataDir_App_); d->visited = new_Visited(); d->bookmarks = new_Bookmarks(); @@ -355,6 +358,7 @@ static void init_App_(iApp *d, int argc, char **argv) { load_Keys(dataDir_App_); load_Visited(d->visited, dataDir_App_); load_Bookmarks(d->bookmarks, dataDir_App_); + load_MimeHooks(d->mimehooks, dataDir_App_); if (isFirstRun) { /* Create the default bookmarks for a quick start. */ add_Bookmarks(d->bookmarks, @@ -433,6 +437,8 @@ static void deinit_App(iApp *d) { save_Visited(d->visited, dataDir_App_); delete_Visited(d->visited); delete_GmCerts(d->certs); + save_MimeHooks(d->mimehooks); + delete_MimeHooks(d->mimehooks); deinit_SortedArray(&d->tickers); delete_Window(d->window); d->window = NULL; @@ -709,6 +715,10 @@ void removeTicker_App(iTickerFunc ticker, iAny *context) { remove_SortedArray(&d->tickers, &(iTicker){ context, ticker }); } +iMimeHooks *mimeHooks_App(void) { + return app_.mimehooks; +} + iGmCerts *certs_App(void) { return app_.certs; } 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. */ iDeclareType(Bookmarks) iDeclareType(DocumentWidget) iDeclareType(GmCerts) +iDeclareType(MimeHooks) iDeclareType(Visited) iDeclareType(Window) @@ -65,6 +66,7 @@ enum iColorTheme colorTheme_App (void); const iString * schemeProxy_App (iRangecc scheme); iBool willUseProxy_App (const iRangecc scheme); +iMimeHooks * mimeHooks_App (void); iGmCerts * certs_App (void); iVisited * visited_App (void); iBookmarks * 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. */ #include "gmcerts.h" #include "gopher.h" #include "app.h" /* dataDir_App() */ +#include "mimehooks.h" #include "feeds.h" #include "ui/text.h" #include "embedded.h" @@ -131,7 +132,8 @@ struct Impl_GmRequest { iTlsRequest * req; iGopher gopher; iGmResponse * resp; - iBool respLocked; + iBool isRespLocked; + iBool isRespFiltered; iAtomicInt allowUpdate; iAudience * updated; iAudience * finished; @@ -164,13 +166,10 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) { } } -static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { - iBool notifyUpdate = iFalse; - iBool notifyDone = iFalse; - lock_Mutex(d->mtx); - iGmResponse *resp =d->resp; - iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ - iBlock *data = readAll_TlsRequest(req); +static int processIncomingData_GmRequest_(iGmRequest *d, const iBlock *data) { + iBool notifyUpdate = iFalse; + iBool notifyDone = iFalse; + iGmResponse *resp = d->resp; if (d->state == receivingHeader_GmRequestState) { appendCStrN_String(&resp->meta, constData_Block(data), size_Block(data)); /* Check if the header line is complete. */ @@ -208,6 +207,9 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { resp->statusCode = code; d->state = receivingBody_GmRequestState; notifyUpdate = iTrue; + if (willTryFilter_MimeHooks(mimeHooks_App(), &resp->meta)) { + d->isRespFiltered = iTrue; + } } checkServerCertificate_GmRequest_(d); iRelease(metaPattern); @@ -217,10 +219,21 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { append_Block(&resp->body, data); notifyUpdate = iTrue; } + return (notifyUpdate ? 1 : 0) | (notifyDone ? 2 : 0); +} + +static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { + lock_Mutex(d->mtx); + iGmResponse *resp = d->resp; + iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ + iBlock * data = readAll_TlsRequest(req); + const int ubits = processIncomingData_GmRequest_(d, data); + iBool notifyUpdate = (ubits & 1) != 0; + iBool notifyDone = (ubits & 2) != 0; initCurrent_Time(&resp->when); delete_Block(data); unlock_Mutex(d->mtx); - if (notifyUpdate) { + if (notifyUpdate && !d->isRespFiltered) { const iBool allowed = exchange_Atomic(&d->allowUpdate, iFalse); if (allowed) { iNotifyAudience(d, updated, GmRequestUpdated); @@ -247,7 +260,18 @@ static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) { set_String(&d->resp->meta, errorMessage_TlsRequest(req)); } checkServerCertificate_GmRequest_(d); - unlock_Mutex(d->mtx); + /* Check for mimehooks. */ + if (d->isRespFiltered && d->state == finished_GmRequestState) { + iBlock *xbody = tryFilter_MimeHooks(mimeHooks_App(), &d->resp->meta, &d->resp->body); + if (xbody) { + clear_String(&d->resp->meta); + clear_Block(&d->resp->body); + d->state = receivingHeader_GmRequestState; + processIncomingData_GmRequest_(d, xbody); + d->state = finished_GmRequestState; + } + } + unlock_Mutex(d->mtx); iNotifyAudience(d, finished, GmRequestFinished); } @@ -425,7 +449,8 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, void init_GmRequest(iGmRequest *d, iGmCerts *certs) { d->mtx = new_Mutex(); d->resp = new_GmResponse(); - d->respLocked = iFalse; + d->isRespLocked = iFalse; + d->isRespFiltered = iFalse; set_Atomic(&d->allowUpdate, iTrue); init_String(&d->url); init_Gopher(&d->gopher); @@ -623,16 +648,16 @@ void cancel_GmRequest(iGmRequest *d) { } iGmResponse *lockResponse_GmRequest(iGmRequest *d) { - iAssert(!d->respLocked); + iAssert(!d->isRespLocked); lock_Mutex(d->mtx); - d->respLocked = iTrue; + d->isRespLocked = iTrue; return d->resp; } void unlockResponse_GmRequest(iGmRequest *d) { if (d) { - iAssert(d->respLocked); - d->respLocked = iFalse; + iAssert(d->isRespLocked); + d->isRespLocked = iFalse; set_Atomic(&d->allowUpdate, iTrue); unlock_Mutex(d->mtx); } 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 @@ +#include "mimehooks.h" + +#include +#include +#include +#include + +iDefineTypeConstruction(FilterHook) + +void init_FilterHook(iFilterHook *d) { + init_String(&d->label); + init_String(&d->mimePattern); + init_String(&d->command); + d->mimeRegex = NULL; +} + +void deinit_FilterHook(iFilterHook *d) { + iRelease(d->mimeRegex); + deinit_String(&d->command); + deinit_String(&d->mimePattern); + deinit_String(&d->label); +} + +void setMimePattern_FilterHook(iFilterHook *d, const iString *pattern) { + iReleasePtr(&d->mimeRegex); + d->mimeRegex = new_RegExp(cstr_String(pattern), caseInsensitive_RegExpOption); +} + +void setCommand_FilterHook(iFilterHook *d, const iString *command) { + set_String(&d->command, command); +} + +iBlock *run_FilterHook_(const iFilterHook *d, const iString *mime, const iBlock *body) { + iProcess * proc = new_Process(); + iStringList *args = new_StringList(); + iRangecc seg = iNullRange; + while (nextSplit_Rangecc(range_String(&d->command), ";", &seg)) { + pushBackRange_StringList(args, seg); + } + seg = iNullRange; + while (nextSplit_Rangecc(range_String(mime), ";", &seg)) { + pushBackRange_StringList(args, seg); + } + setArguments_Process(proc, args); + iRelease(args); + start_Process(proc); + writeInput_Process(proc, body); + waitForFinished_Process(proc); + iBlock *output = readOutput_Process(proc); + if (!startsWith_Rangecc(range_Block(output), "20")) { + /* Didn't produce valid output. */ + delete_Block(output); + output = NULL; + } + iRelease(proc); + return output; +} + +/*----------------------------------------------------------------------------------------------*/ + +struct Impl_MimeHooks { + iPtrArray filters; +}; + +iDefineTypeConstruction(MimeHooks) + +void init_MimeHooks(iMimeHooks *d) { + init_PtrArray(&d->filters); +} + +void deinit_MimeHooks(iMimeHooks *d) { + iForEach(PtrArray, i, &d->filters) { + delete_FilterHook(i.ptr); + } + deinit_PtrArray(&d->filters); +} + +iBool willTryFilter_MimeHooks(const iMimeHooks *d, const iString *mime) { + /* TODO: Combine this function with tryFilter_MimeHooks? */ + iRegExpMatch m; + iConstForEach(PtrArray, i, &d->filters) { + const iFilterHook *xc = i.ptr; + init_RegExpMatch(&m); + if (matchString_RegExp(xc->mimeRegex, mime, &m)) { + return iTrue; + } + } + return iFalse; +} + +iBlock *tryFilter_MimeHooks(const iMimeHooks *d, const iString *mime, const iBlock *body) { + iRegExpMatch m; + iConstForEach(PtrArray, i, &d->filters) { + const iFilterHook *xc = i.ptr; + init_RegExpMatch(&m); + if (matchString_RegExp(xc->mimeRegex, mime, &m)) { + iBlock *result = run_FilterHook_(xc, mime, body); + if (result) { + return result; + } + } + } + return NULL; +} + +static const char *mimeHooksFilename_MimeHooks_ = "mimehooks.txt"; + +void load_MimeHooks(iMimeHooks *d, const char *saveDir) { + iFile *f = newCStr_File(concatPath_CStr(saveDir, mimeHooksFilename_MimeHooks_)); + if (open_File(f, read_FileMode | text_FileMode)) { + iBlock * src = readAll_File(f); + iRangecc srcLine = iNullRange; + int pos = 0; + iRangecc lines[3]; + iZap(lines); + while (nextSplit_Rangecc(range_Block(src), "\n", &srcLine)) { + iRangecc line = srcLine; + trim_Rangecc(&line); + if (isEmpty_Range(&line)) { + continue; + } + lines[pos++] = line; + if (pos == 3) { + iFilterHook *hook = new_FilterHook(); + setRange_String(&hook->label, lines[0]); + setMimePattern_FilterHook(hook, collect_String(newRange_String(lines[1]))); + setCommand_FilterHook(hook, collect_String(newRange_String(lines[2]))); + pushBack_PtrArray(&d->filters, hook); + } + } + delete_Block(src); + } + iRelease(f); +} + +void save_MimeHooks(const iMimeHooks *d) { + iUnused(d); +} + 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 @@ +#pragma once + +#include +#include +#include +#include + +iDeclareType(FilterHook) +iDeclareTypeConstruction(FilterHook) + +struct Impl_FilterHook { + iString label; + iString mimePattern; + iRegExp *mimeRegex; + iString command; +}; + +void setMimePattern_FilterHook (iFilterHook *, const iString *pattern); +void setCommand_FilterHook (iFilterHook *, const iString *command); + +/*----------------------------------------------------------------------------------------------*/ + +iDeclareType(MimeHooks) +iDeclareTypeConstruction(MimeHooks) + +iBool willTryFilter_MimeHooks (const iMimeHooks *, const iString *mime); +iBlock * tryFilter_MimeHooks (const iMimeHooks *, const iString *mime, + const iBlock *body); + +void load_MimeHooks (iMimeHooks *, const char *saveDir); +void save_MimeHooks (const iMimeHooks *); -- cgit v1.2.3