summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-08 22:02:14 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-08 22:02:58 +0300
commit496122b446d2084f6117e2b8704d7c9ec6706422 (patch)
treeb379efe7f6a2ed530ec9a9bc982e7f48cbd3a29a
parent7b98d7af353c31706d5d6b20ff7d65272f8b7445 (diff)
Handling "about:home"; GmRequest decoupling
GmRequest is given a pointer to the GmCerts to use. Also, timeouts are handled via a posted command so they get processed in the main thread.
-rw-r--r--src/app.c12
-rw-r--r--src/app.h1
-rw-r--r--src/gmrequest.c82
-rw-r--r--src/gmrequest.h5
-rw-r--r--src/gmutil.c16
-rw-r--r--src/gmutil.h5
-rw-r--r--src/ui/documentwidget.c12
7 files changed, 98 insertions, 35 deletions
diff --git a/src/app.c b/src/app.c
index e88abe20..fa7311c6 100644
--- a/src/app.c
+++ b/src/app.c
@@ -56,7 +56,6 @@ struct Impl_App {
56 iSortedArray tickers; 56 iSortedArray tickers;
57 iBool pendingRefresh; 57 iBool pendingRefresh;
58 iGmCerts * certs; 58 iGmCerts * certs;
59// iHistory * history;
60 iVisited * visited; 59 iVisited * visited;
61 /* Preferences: */ 60 /* Preferences: */
62 iBool retainWindowSize; 61 iBool retainWindowSize;
@@ -185,6 +184,10 @@ const iString *execPath_App(void) {
185 return executablePath_CommandLine(&app_.args); 184 return executablePath_CommandLine(&app_.args);
186} 185}
187 186
187const iString *dataDir_App(void) {
188 return collect_String(cleanedCStr_Path(dataDir_App_));
189}
190
188void processEvents_App(enum iAppEventMode eventMode) { 191void processEvents_App(enum iAppEventMode eventMode) {
189 iApp *d = &app_; 192 iApp *d = &app_;
190 SDL_Event ev; 193 SDL_Event ev;
@@ -423,12 +426,7 @@ iBool handleCommand_App(const char *cmd) {
423 SDL_SetWindowPosition(d->window->win, pos.x, pos.y); 426 SDL_SetWindowPosition(d->window->win, pos.x, pos.y);
424 } 427 }
425 else if (equal_Command(cmd, "navigate.home")) { 428 else if (equal_Command(cmd, "navigate.home")) {
426 iString *homePath = newCStr_String(dataDir_App_); 429 postCommand_App("open url:about:home");
427 clean_Path(homePath);
428 append_Path(homePath, &iStringLiteral("home.gmi"));
429 prependCStr_String(homePath, "file://");
430 postCommandf_App("open url:%s", cstr_String(homePath));
431 delete_String(homePath);
432 return iTrue; 430 return iTrue;
433 } 431 }
434 else if (equal_Command(cmd, "font.setfactor")) { 432 else if (equal_Command(cmd, "font.setfactor")) {
diff --git a/src/app.h b/src/app.h
index d10da7c3..e40c5b2e 100644
--- a/src/app.h
+++ b/src/app.h
@@ -21,6 +21,7 @@ enum iUserEventCode {
21}; 21};
22 22
23const iString *execPath_App (void); 23const iString *execPath_App (void);
24const iString *dataDir_App (void);
24 25
25int run_App (int argc, char **argv); 26int run_App (int argc, char **argv);
26void processEvents_App (enum iAppEventMode mode); 27void processEvents_App (enum iAppEventMode mode);
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 7cc8b897..1a6476ba 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -1,11 +1,12 @@
1#include "gmrequest.h" 1#include "gmrequest.h"
2#include "gmutil.h" 2#include "gmutil.h"
3#include "gmcerts.h" 3#include "gmcerts.h"
4#include "app.h" 4#include "app.h" /* dataDir_App() */
5 5
6#include <the_Foundation/file.h> 6#include <the_Foundation/file.h>
7#include <the_Foundation/tlsrequest.h> 7#include <the_Foundation/tlsrequest.h>
8#include <the_Foundation/mutex.h> 8#include <the_Foundation/mutex.h>
9#include <the_Foundation/path.h>
9 10
10#include <SDL_timer.h> 11#include <SDL_timer.h>
11 12
@@ -62,30 +63,35 @@ enum iGmRequestState {
62}; 63};
63 64
64struct Impl_GmRequest { 65struct Impl_GmRequest {
65 iObject object; 66 iObject object;
66 iMutex mutex; 67 iMutex mutex;
67 enum iGmRequestState state; 68 iGmCerts * certs; /* not owned */
68 iString url; 69 enum iGmRequestState state;
69 iTlsRequest *req; 70 iString url;
70 iGmResponse resp; 71 iTlsRequest * req;
71 uint32_t timeoutId; /* in case server doesn't close the connection */ 72 iGmResponse resp;
72 iAudience *updated; 73 uint32_t timeoutId; /* in case server doesn't close the connection */
73 iAudience *finished; 74 iAudience * updated;
75 iAudience * timeout;
76 iAudience * finished;
74}; 77};
75 78
76iDefineObjectConstruction(GmRequest) 79iDefineObjectConstructionArgs(GmRequest, (iGmCerts *certs), certs)
77iDefineAudienceGetter(GmRequest, updated) 80iDefineAudienceGetter(GmRequest, updated)
81iDefineAudienceGetter(GmRequest, timeout)
78iDefineAudienceGetter(GmRequest, finished) 82iDefineAudienceGetter(GmRequest, finished)
79 83
80void init_GmRequest(iGmRequest *d) { 84void init_GmRequest(iGmRequest *d, iGmCerts *certs) {
81 init_Mutex(&d->mutex); 85 init_Mutex(&d->mutex);
82 d->state = initialized_GmRequestState;
83 init_GmResponse(&d->resp); 86 init_GmResponse(&d->resp);
84 init_String(&d->url); 87 init_String(&d->url);
85 d->req = NULL; 88 d->certs = certs;
86 d->timeoutId = 0; 89 d->timeoutId = 0;
87 d->updated = NULL; 90 d->req = NULL;
88 d->finished = NULL; 91 d->state = initialized_GmRequestState;
92 d->updated = NULL;
93 d->timeout = NULL;
94 d->finished = NULL;
89} 95}
90 96
91void deinit_GmRequest(iGmRequest *d) { 97void deinit_GmRequest(iGmRequest *d) {
@@ -107,6 +113,7 @@ void deinit_GmRequest(iGmRequest *d) {
107 } 113 }
108 iRelease(d->req); 114 iRelease(d->req);
109 d->req = NULL; 115 d->req = NULL;
116 delete_Audience(d->timeout);
110 delete_Audience(d->finished); 117 delete_Audience(d->finished);
111 delete_Audience(d->updated); 118 delete_Audience(d->updated);
112 deinit_GmResponse(&d->resp); 119 deinit_GmResponse(&d->resp);
@@ -121,7 +128,9 @@ void setUrl_GmRequest(iGmRequest *d, const iString *url) {
121 128
122static uint32_t timedOutWhileReceivingBody_GmRequest_(uint32_t interval, void *obj) { 129static uint32_t timedOutWhileReceivingBody_GmRequest_(uint32_t interval, void *obj) {
123 /* Note: Called from SDL's timer thread. */ 130 /* Note: Called from SDL's timer thread. */
124 postCommandf_App("gmrequest.timeout request:%p", obj); 131 iGmRequest *d = obj;
132 //postCommandf_App("gmrequest.timeout request:%p", obj);
133 iNotifyAudience(d, timeout, GmRequestTimeout);
125 iUnused(interval); 134 iUnused(interval);
126 return 0; 135 return 0;
127} 136}
@@ -142,7 +151,6 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
142 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); 151 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req);
143 d->resp.certFlags = 0; 152 d->resp.certFlags = 0;
144 if (cert) { 153 if (cert) {
145 iGmCerts * certDb = certs_App();
146 const iRangecc domain = urlHost_String(&d->url); 154 const iRangecc domain = urlHost_String(&d->url);
147 d->resp.certFlags |= available_GmCertFlag; 155 d->resp.certFlags |= available_GmCertFlag;
148 if (!isExpired_TlsCertificate(cert)) { 156 if (!isExpired_TlsCertificate(cert)) {
@@ -151,7 +159,7 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
151 if (verifyDomain_TlsCertificate(cert, domain)) { 159 if (verifyDomain_TlsCertificate(cert, domain)) {
152 d->resp.certFlags |= domainVerified_GmCertFlag; 160 d->resp.certFlags |= domainVerified_GmCertFlag;
153 } 161 }
154 if (checkTrust_GmCerts(certDb, domain, cert)) { 162 if (checkTrust_GmCerts(d->certs, domain, cert)) {
155 d->resp.certFlags |= trusted_GmCertFlag; 163 d->resp.certFlags |= trusted_GmCertFlag;
156 } 164 }
157 validUntil_TlsCertificate(cert, &d->resp.certValidUntil); 165 validUntil_TlsCertificate(cert, &d->resp.certValidUntil);
@@ -243,7 +251,39 @@ void submit_GmRequest(iGmRequest *d) {
243 clear_GmResponse(&d->resp); 251 clear_GmResponse(&d->resp);
244 iUrl url; 252 iUrl url;
245 init_Url(&url, &d->url); 253 init_Url(&url, &d->url);
246 if (equalCase_Rangecc(&url.protocol, "file")) { 254 /* Check for special protocols. */
255 /* TODO: If this were a library, these could be handled via callbacks. */
256 if (equalCase_Rangecc(&url.protocol, "about")) {
257 if (equalCase_Rangecc(&url.path, "home")) {
258 d->resp.statusCode = success_GmStatusCode;
259 setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8");
260 setCStr_Block(&d->resp.body,
261"```\n"
262"ooooo \n"
263"`888' \n"
264" 888 .oooo. .oooooooo oooo d8b .oooo. ooo. .oo. .oooooooo .ooooo. \n"
265" 888 `P )88b 888' `88b `888\"\"8P `P )88b `888P\"Y88b 888' `88b d88' `88b \n"
266" 888 .oP\"888 888 888 888 .oP\"888 888 888 888 888 888ooo888 \n"
267" 888 o d8( 888 `88bod8P' 888 d8( 888 888 888 `88bod8P' 888 .o \n"
268"o888ooooood8 `Y888\"\"8o `8oooooo. d888b `Y888\"\"8o o888o o888o `8oooooo. `Y8bod8P' \n"
269" d\" YD d\" YD \n"
270" \"Y88888P' \"Y88888P' \n"
271"```\n"
272"# A Beautiful Gemini Browser\n"
273"## Version " LAGRANGE_APP_VERSION "\n\n"
274"=> https://skyjake.fi/@jk by Jaakko Keränen <code@iki.fi>\n"
275"Crafted with \u2615 in Finland\n");
276 d->state = receivingBody_GmRequestState;
277 iNotifyAudience(d, updated, GmRequestUpdated);
278 }
279 else {
280 d->resp.statusCode = invalidLocalResource_GmStatusCode;
281 }
282 d->state = finished_GmRequestState;
283 iNotifyAudience(d, finished, GmRequestFinished);
284 return;
285 }
286 else if (equalCase_Rangecc(&url.protocol, "file")) {
247 iString *path = collect_String(urlDecode_String(collect_String(newRange_String(url.path)))); 287 iString *path = collect_String(urlDecode_String(collect_String(newRange_String(url.path))));
248 iFile * f = new_File(path); 288 iFile * f = new_File(path);
249 if (open_File(f, readOnly_FileMode)) { 289 if (open_File(f, readOnly_FileMode)) {
diff --git a/src/gmrequest.h b/src/gmrequest.h
index 3b2ee3b3..05290128 100644
--- a/src/gmrequest.h
+++ b/src/gmrequest.h
@@ -5,6 +5,7 @@
5 5
6#include "gmutil.h" 6#include "gmutil.h"
7 7
8iDeclareType(GmCerts)
8iDeclareType(GmResponse) 9iDeclareType(GmResponse)
9 10
10enum iGmCertFlags { 11enum iGmCertFlags {
@@ -30,11 +31,13 @@ iGmResponse * copy_GmResponse (const iGmResponse *);
30/*----------------------------------------------------------------------------------------------*/ 31/*----------------------------------------------------------------------------------------------*/
31 32
32iDeclareClass(GmRequest) 33iDeclareClass(GmRequest)
33iDeclareObjectConstruction(GmRequest) 34iDeclareObjectConstructionArgs(GmRequest, iGmCerts *)
34 35
35iDeclareNotifyFunc(GmRequest, Updated) 36iDeclareNotifyFunc(GmRequest, Updated)
37iDeclareNotifyFunc(GmRequest, Timeout)
36iDeclareNotifyFunc(GmRequest, Finished) 38iDeclareNotifyFunc(GmRequest, Finished)
37iDeclareAudienceGetter(GmRequest, updated) 39iDeclareAudienceGetter(GmRequest, updated)
40iDeclareAudienceGetter(GmRequest, timeout)
38iDeclareAudienceGetter(GmRequest, finished) 41iDeclareAudienceGetter(GmRequest, finished)
39 42
40void setUrl_GmRequest (iGmRequest *, const iString *url); 43void setUrl_GmRequest (iGmRequest *, const iString *url);
diff --git a/src/gmutil.c b/src/gmutil.c
index 0caadd32..4b49e1d0 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -2,6 +2,7 @@
2 2
3#include <the_Foundation/regexp.h> 3#include <the_Foundation/regexp.h>
4#include <the_Foundation/object.h> 4#include <the_Foundation/object.h>
5#include <the_Foundation/path.h>
5 6
6void init_Url(iUrl *d, const iString *text) { 7void init_Url(iUrl *d, const iString *text) {
7 iRegExp *absPat = 8 iRegExp *absPat =
@@ -142,6 +143,13 @@ const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelat
142 return absolute; 143 return absolute;
143} 144}
144 145
146iString *makeFileUrl_String(const iString *localFilePath) {
147 iString *url = cleaned_Path(localFilePath);
148 replace_Block(&url->chars, '\\', '/'); /* in case it's a Windows path */
149 prependCStr_String(url, "file://");
150 return url;
151}
152
145void urlEncodeSpaces_String(iString *d) { 153void urlEncodeSpaces_String(iString *d) {
146 for (;;) { 154 for (;;) {
147 const size_t pos = indexOfCStr_String(d, " "); 155 const size_t pos = indexOfCStr_String(d, " ");
@@ -166,6 +174,10 @@ static const struct {
166 "Failed to Open File", 174 "Failed to Open File",
167 "The requested file does not exist or is inaccessible. " 175 "The requested file does not exist or is inaccessible. "
168 "Please check the file path." } }, 176 "Please check the file path." } },
177 { invalidLocalResource_GmStatusCode,
178 { 0,
179 "Invalid Resource",
180 "The requested resource does not exist." } },
169 { unsupportedMimeType_GmStatusCode, 181 { unsupportedMimeType_GmStatusCode,
170 { 0x1f47d, /* alien */ 182 { 0x1f47d, /* alien */
171 "Unsupported MIME Type", 183 "Unsupported MIME Type",
@@ -178,7 +190,7 @@ static const struct {
178 "Perhaps the server is malfunctioning or you tried to contact a " 190 "Perhaps the server is malfunctioning or you tried to contact a "
179 "non-Gemini server." } }, 191 "non-Gemini server." } },
180 { invalidRedirect_GmStatusCode, 192 { invalidRedirect_GmStatusCode,
181 { 0, /* */ 193 { 0x27a0, /* dashed arrow */
182 "Invalid Redirect", 194 "Invalid Redirect",
183 "The server responded with a redirect but did not provide a valid destination URL. " 195 "The server responded with a redirect but did not provide a valid destination URL. "
184 "Perhaps the server is malfunctioning." } }, 196 "Perhaps the server is malfunctioning." } },
@@ -226,7 +238,7 @@ static const struct {
226 { 0x1f44e, /* thumbs down */ 238 { 0x1f44e, /* thumbs down */
227 "Bad Request", 239 "Bad Request",
228 "The server was unable to parse your request, presumably due to the " 240 "The server was unable to parse your request, presumably due to the "
229 "request being malformed. Likely a bug in Lagrange." } }, 241 "request being malformed." } },
230 { clientCertificateRequired_GmStatusCode, 242 { clientCertificateRequired_GmStatusCode,
231 { 0x1f511, /* key */ 243 { 0x1f511, /* key */
232 "Certificate Required", 244 "Certificate Required",
diff --git a/src/gmutil.h b/src/gmutil.h
index d0ed4581..8dda84d0 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -3,7 +3,8 @@
3#include <the_Foundation/range.h> 3#include <the_Foundation/range.h>
4#include <the_Foundation/string.h> 4#include <the_Foundation/string.h>
5 5
6iDeclareType(GmError) iDeclareType(Url) 6iDeclareType(GmError)
7iDeclareType(Url)
7 8
8/* Response status codes. */ 9/* Response status codes. */
9enum iGmStatusCode { 10enum iGmStatusCode {
@@ -14,6 +15,7 @@ enum iGmStatusCode {
14 unsupportedMimeType_GmStatusCode, 15 unsupportedMimeType_GmStatusCode,
15 failedToOpenFile_GmStatusCode, 16 failedToOpenFile_GmStatusCode,
16 unknownStatusCode_GmStatusCode, 17 unknownStatusCode_GmStatusCode,
18 invalidLocalResource_GmStatusCode,
17 none_GmStatusCode = 0, 19 none_GmStatusCode = 0,
18 /* general status code categories */ 20 /* general status code categories */
19 categoryInput_GmStatusCode = 1, 21 categoryInput_GmStatusCode = 1,
@@ -70,4 +72,5 @@ void init_Url (iUrl *, const iString *text);
70 72
71iRangecc urlHost_String (const iString *); 73iRangecc urlHost_String (const iString *);
72const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); 74const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative);
75iString * makeFileUrl_String (const iString *localFilePath);
73void urlEncodeSpaces_String (iString *); 76void urlEncodeSpaces_String (iString *);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 8cc47e2a..a789a759 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -55,7 +55,7 @@ static void finished_MediaRequest_(iAnyObject *obj) {
55void init_MediaRequest(iMediaRequest *d, iDocumentWidget *doc, iGmLinkId linkId, const iString *url) { 55void init_MediaRequest(iMediaRequest *d, iDocumentWidget *doc, iGmLinkId linkId, const iString *url) {
56 d->doc = doc; 56 d->doc = doc;
57 d->linkId = linkId; 57 d->linkId = linkId;
58 d->req = new_GmRequest(); 58 d->req = new_GmRequest(certs_App());
59 setUrl_GmRequest(d->req, url); 59 setUrl_GmRequest(d->req, url);
60 iConnect(GmRequest, d->req, updated, d, updated_MediaRequest_); 60 iConnect(GmRequest, d->req, updated, d, updated_MediaRequest_);
61 iConnect(GmRequest, d->req, finished, d, finished_MediaRequest_); 61 iConnect(GmRequest, d->req, finished, d, finished_MediaRequest_);
@@ -202,6 +202,11 @@ static void requestUpdated_DocumentWidget_(iAnyObject *obj) {
202 } 202 }
203} 203}
204 204
205static void requestTimedOut_DocumentWidget_(iAnyObject *obj) {
206 iDocumentWidget *d = obj;
207 postCommandf_App("document.request.timeout doc:%p request:%p", d, d->request);
208}
209
205static void requestFinished_DocumentWidget_(iAnyObject *obj) { 210static void requestFinished_DocumentWidget_(iAnyObject *obj) {
206 iDocumentWidget *d = obj; 211 iDocumentWidget *d = obj;
207 postCommand_Widget(obj, "document.request.finished doc:%p request:%p", d, d->request); 212 postCommand_Widget(obj, "document.request.finished doc:%p request:%p", d, d->request);
@@ -402,9 +407,10 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) {
402 d->certFlags = 0; 407 d->certFlags = 0;
403 d->state = fetching_DocumentState; 408 d->state = fetching_DocumentState;
404 set_Atomic(&d->isRequestUpdated, iFalse); 409 set_Atomic(&d->isRequestUpdated, iFalse);
405 d->request = new_GmRequest(); 410 d->request = new_GmRequest(certs_App());
406 setUrl_GmRequest(d->request, d->url); 411 setUrl_GmRequest(d->request, d->url);
407 iConnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_); 412 iConnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_);
413 iConnect(GmRequest, d->request, timeout, d, requestTimedOut_DocumentWidget_);
408 iConnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_); 414 iConnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_);
409 submit_GmRequest(d->request); 415 submit_GmRequest(d->request);
410} 416}
@@ -792,7 +798,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
792 postCommandf_App("document.changed url:%s", cstr_String(d->url)); 798 postCommandf_App("document.changed url:%s", cstr_String(d->url));
793 return iFalse; 799 return iFalse;
794 } 800 }
795 else if (equal_Command(cmd, "gmrequest.timeout") && 801 else if (equal_Command(cmd, "document.request.timeout") &&
796 pointerLabel_Command(cmd, "request") == d->request) { 802 pointerLabel_Command(cmd, "request") == d->request) {
797 cancel_GmRequest(d->request); 803 cancel_GmRequest(d->request);
798 return iFalse; 804 return iFalse;