summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Embed.cmake2
-rw-r--r--src/app.c15
-rw-r--r--src/gmcerts.c121
-rw-r--r--src/gmcerts.h2
-rw-r--r--src/gmdocument.h2
-rw-r--r--src/gmrequest.c47
-rw-r--r--src/gmrequest.h17
-rw-r--r--src/gmutil.c7
-rw-r--r--src/gmutil.h1
-rw-r--r--src/ui/documentwidget.c35
10 files changed, 220 insertions, 29 deletions
diff --git a/Embed.cmake b/Embed.cmake
index 16c47683..810c947b 100644
--- a/Embed.cmake
+++ b/Embed.cmake
@@ -69,6 +69,7 @@ function (embed_make)
69 endif () 69 endif ()
70 if (needGen) 70 if (needGen)
71 if (EMBED_IN_EXECUTABLE) 71 if (EMBED_IN_EXECUTABLE)
72 # Compose a source file with the resource data in an array.
72 file (WRITE ${EMB_H} "#include <the_Foundation/block.h>\n") 73 file (WRITE ${EMB_H} "#include <the_Foundation/block.h>\n")
73 file (WRITE ${EMB_C} "#include \"embedded.h\"\n") 74 file (WRITE ${EMB_C} "#include \"embedded.h\"\n")
74 foreach (fn ${ARGV}) 75 foreach (fn ${ARGV})
@@ -76,6 +77,7 @@ function (embed_make)
76 embed_write (${fn} ${resName} ${EMB_C} ${EMB_H}) 77 embed_write (${fn} ${resName} ${EMB_C} ${EMB_H})
77 endforeach (fn) 78 endforeach (fn)
78 else () 79 else ()
80 # Collect resources in a single binary file.
79 set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.bin) 81 set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.bin)
80 file (REMOVE ${EMB_BIN}) 82 file (REMOVE ${EMB_BIN})
81 execute_process (COMMAND cat ${ARGV} OUTPUT_FILE ${EMB_BIN} 83 execute_process (COMMAND cat ${ARGV} OUTPUT_FILE ${EMB_BIN}
diff --git a/src/app.c b/src/app.c
index 46235206..21028edc 100644
--- a/src/app.c
+++ b/src/app.c
@@ -41,7 +41,8 @@ static const char *dataDir_App_ = "~/Library/Application Support/fi.skyjake.Lagr
41static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange"; 41static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange";
42#endif 42#endif
43#if defined (iPlatformLinux) 43#if defined (iPlatformLinux)
44#define EMB_BIN "../../share/lagrange/resources.bin" 44#define EMB_BIN "../../share/lagrange/resources.bin"
45#define EMB_BIN2 "../resources.bin" /* try from build dir as well */
45static const char *dataDir_App_ = "~/.config/lagrange"; 46static const char *dataDir_App_ = "~/.config/lagrange";
46#endif 47#endif
47static const char *prefsFileName_App_ = "prefs.cfg"; 48static const char *prefsFileName_App_ = "prefs.cfg";
@@ -152,9 +153,11 @@ static void init_App_(iApp *d, int argc, char **argv) {
152 load_History(d->history, dataDir_App_); 153 load_History(d->history, dataDir_App_);
153#if defined (iHaveLoadEmbed) 154#if defined (iHaveLoadEmbed)
154 /* Load the resources from a file. */ { 155 /* Load the resources from a file. */ {
155 if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), EMB_BIN))) { 156 if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), "../resources.bin"))) {
156 fprintf(stderr, "failed to load resources.bin\n"); 157 if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), EMB_BIN))) {
157 exit(-1); 158 fprintf(stderr, "failed to load resources.bin: %s\n", strerror(errno));
159 exit(-1);
160 }
158 } 161 }
159 } 162 }
160#endif 163#endif
@@ -311,6 +314,10 @@ void addTicker_App(void (*ticker)(iAny *), iAny *context) {
311 insert_SortedArray(&d->tickers, &(iTicker){ context, ticker }); 314 insert_SortedArray(&d->tickers, &(iTicker){ context, ticker });
312} 315}
313 316
317iGmCerts *certs_App(void) {
318 return app_.certs;
319}
320
314const iHistory *history_App(void) { 321const iHistory *history_App(void) {
315 return app_.history; 322 return app_.history;
316} 323}
diff --git a/src/gmcerts.c b/src/gmcerts.c
index cb4d8477..0aa084a4 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -1,15 +1,134 @@
1#include "gmcerts.h" 1#include "gmcerts.h"
2 2
3#include <the_Foundation/file.h>
4#include <the_Foundation/path.h>
5#include <the_Foundation/regexp.h>
6#include <the_Foundation/stringhash.h>
7#include <the_Foundation/time.h>
8#include <ctype.h>
9
10static const char *filename_GmCerts_ = "trusted.txt";
11
12iDeclareClass(TrustEntry)
13
14struct Impl_TrustEntry {
15 iObject object;
16 iBlock fingerprint;
17 iTime validUntil;
18};
19
20void init_TrustEntry(iTrustEntry *d, const iBlock *fingerprint, const iDate *until) {
21 initCopy_Block(&d->fingerprint, fingerprint);
22 init_Time(&d->validUntil, until);
23}
24
25void deinit_TrustEntry(iTrustEntry *d) {
26 deinit_Block(&d->fingerprint);
27}
28
29iDefineObjectConstructionArgs(TrustEntry, (const iBlock *fingerprint, const iDate *until), fingerprint, until)
30iDefineClass(TrustEntry)
31
32/*-----------------------------------------------------------------------------------------------*/
33
3struct Impl_GmCerts { 34struct Impl_GmCerts {
4 iString saveDir; 35 iString saveDir;
36 iStringHash *trusted;
5}; 37};
6 38
7iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir) 39iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir)
8 40
41static void save_GmCerts_(const iGmCerts *d) {
42 iBeginCollect();
43 iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_)));
44 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
45 iString line;
46 init_String(&line);
47 iConstForEach(StringHash, i, d->trusted) {
48 const iTrustEntry *trust = value_StringHashNode(i.value);
49 format_String(&line,
50 "%s %ld %s\n",
51 cstr_String(key_StringHashConstIterator(&i)),
52 integralSeconds_Time(&trust->validUntil),
53 cstrCollect_String(hexEncode_Block(&trust->fingerprint)));
54 write_File(f, &line.chars);
55 }
56 deinit_String(&line);
57 }
58 iRelease(f);
59 iEndCollect();
60}
61
62static void load_GmCerts_(iGmCerts *d) {
63 iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_)));
64 if (open_File(f, readOnly_FileMode | text_FileMode)) {
65 iRegExp * pattern = new_RegExp("([^\\s]+) ([0-9]+) ([a-z0-9]+)", 0);
66 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
67 iRangecc line = iNullRange;
68 while (nextSplit_Rangecc(&src, "\n", &line)) {
69 iRegExpMatch m;
70 if (matchRange_RegExp(pattern, line, &m)) {
71 const iRangecc domain = capturedRange_RegExpMatch(&m, 1);
72 const iRangecc until = capturedRange_RegExpMatch(&m, 2);
73 const iRangecc fp = capturedRange_RegExpMatch(&m, 3);
74 time_t sec;
75 sscanf(until.start, "%ld", &sec);
76 iDate untilDate;
77 initSinceEpoch_Date(&untilDate, sec);
78 insert_StringHash(d->trusted,
79 collect_String(newRange_String(domain)),
80 new_TrustEntry(collect_Block(hexDecode_Rangecc(fp)),
81 &untilDate));
82 }
83 }
84 iRelease(pattern);
85 }
86 iRelease(f);
87}
88
9void init_GmCerts(iGmCerts *d, const char *saveDir) { 89void init_GmCerts(iGmCerts *d, const char *saveDir) {
10 initCStr_String(&d->saveDir, saveDir); 90 initCStr_String(&d->saveDir, saveDir);
91 d->trusted = new_StringHash();
92 load_GmCerts_(d);
11} 93}
12 94
13void deinit_GmCerts(iGmCerts *d) { 95void deinit_GmCerts(iGmCerts *d) {
96 iRelease(d->trusted);
14 deinit_String(&d->saveDir); 97 deinit_String(&d->saveDir);
15} 98}
99
100iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *cert) {
101 if (!cert) {
102 return iFalse;
103 }
104 if (isExpired_TlsCertificate(cert)) {
105 return iFalse;
106 }
107 if (!verifyDomain_TlsCertificate(cert, domain)) {
108 return iFalse;
109 }
110 /* Good certificate. If not already trusted, add it now. */
111 const iString *key = collect_String(newRange_String(domain));
112 iDate until;
113 validUntil_TlsCertificate(cert, &until);
114 iBlock *fingerprint = collect_Block(fingerprint_TlsCertificate(cert));
115 iTrustEntry *trust = value_StringHash(d->trusted, key);
116 if (trust) {
117 /* We already have it, check if it matches the one we trust for this domain (if it's
118 still valid. */
119 iTime now;
120 initCurrent_Time(&now);
121 if (secondsSince_Time(&trust->validUntil, &now) > 0) {
122 /* Trusted cert is still valid. */
123 return cmp_Block(fingerprint, &trust->fingerprint) == 0;
124 }
125 /* Update the trusted cert. */
126 init_Time(&trust->validUntil, &until);
127 set_Block(&trust->fingerprint, fingerprint);
128 }
129 else {
130 insert_StringHash(d->trusted, key, iClob(new_TrustEntry(fingerprint, &until)));
131 }
132 save_GmCerts_(d);
133 return iTrue;
134}
diff --git a/src/gmcerts.h b/src/gmcerts.h
index 1990463c..a3df8f33 100644
--- a/src/gmcerts.h
+++ b/src/gmcerts.h
@@ -4,3 +4,5 @@
4 4
5iDeclareType(GmCerts) 5iDeclareType(GmCerts)
6iDeclareTypeConstructionArgs(GmCerts, const char *saveDir) 6iDeclareTypeConstructionArgs(GmCerts, const char *saveDir)
7
8iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const iTlsCertificate *cert);
diff --git a/src/gmdocument.h b/src/gmdocument.h
index db5a5451..215dcdb5 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -1,6 +1,6 @@
1#pragma once 1#pragma once
2 2
3#include "gemini.h" 3#include "gmutil.h"
4#include <the_Foundation/object.h> 4#include <the_Foundation/object.h>
5#include <the_Foundation/rect.h> 5#include <the_Foundation/rect.h>
6#include <the_Foundation/string.h> 6#include <the_Foundation/string.h>
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 32b386b2..dc08d23b 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -1,5 +1,7 @@
1#include "gmrequest.h" 1#include "gmrequest.h"
2#include "gmutil.h" 2#include "gmutil.h"
3#include "gmcerts.h"
4#include "app.h"
3 5
4#include <the_Foundation/file.h> 6#include <the_Foundation/file.h>
5#include <the_Foundation/tlsrequest.h> 7#include <the_Foundation/tlsrequest.h>
@@ -20,7 +22,7 @@ struct Impl_GmRequest {
20 iObject object; 22 iObject object;
21 iMutex mutex; 23 iMutex mutex;
22 enum iGmRequestState state; 24 enum iGmRequestState state;
23 enum iGmRequestCertification certState; 25 int certFlags;
24 iString url; 26 iString url;
25 iTlsRequest *req; 27 iTlsRequest *req;
26 enum iGmStatusCode code; 28 enum iGmStatusCode code;
@@ -38,7 +40,7 @@ iDefineAudienceGetter(GmRequest, finished)
38void init_GmRequest(iGmRequest *d) { 40void init_GmRequest(iGmRequest *d) {
39 init_Mutex(&d->mutex); 41 init_Mutex(&d->mutex);
40 d->state = initialized_GmRequestState; 42 d->state = initialized_GmRequestState;
41 d->certState = notApplicable_GmRequestCertification; 43 d->certFlags = 0;
42 init_String(&d->url); 44 init_String(&d->url);
43 d->req = NULL; 45 d->req = NULL;
44 d->code = none_GmStatusCode; 46 d->code = none_GmStatusCode;
@@ -165,11 +167,25 @@ static void requestFinished_GmRequest_(iAnyObject *obj) {
165 } 167 }
166 SDL_RemoveTimer(d->timeoutId); 168 SDL_RemoveTimer(d->timeoutId);
167 d->timeoutId = 0; 169 d->timeoutId = 0;
168 d->state = finished_GmRequestState; 170 d->state = finished_GmRequestState;
169#if 1 171 d->certFlags = 0;
170 /* Check the server certificate. */ { 172 /* Check the server certificate. */ {
171 d->certState = invalid_GmRequestCertification; /* check trust */
172 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); 173 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req);
174 if (cert) {
175 iGmCerts * certDb = certs_App();
176 const iRangecc domain = urlHost_String(&d->url);
177 d->certFlags |= available_GmRequestCertFlag;
178 if (!isExpired_TlsCertificate(cert)) {
179 d->certFlags |= timeVerified_GmRequestCertFlag;
180 }
181 if (verifyDomain_TlsCertificate(cert, domain)) {
182 d->certFlags |= domainVerified_GmRequestCertFlag;
183 }
184 if (checkTrust_GmCerts(certDb, domain, cert)) {
185 d->certFlags |= trusted_GmRequestCertFlag;
186 }
187 }
188#if 0
173 printf("Server certificate:\n%s\n", cstrLocal_String(pem_TlsCertificate(cert))); 189 printf("Server certificate:\n%s\n", cstrLocal_String(pem_TlsCertificate(cert)));
174 iBlock *sha = fingerprint_TlsCertificate(cert); 190 iBlock *sha = fingerprint_TlsCertificate(cert);
175 printf("Fingerprint: %s\n", 191 printf("Fingerprint: %s\n",
@@ -188,8 +204,8 @@ static void requestFinished_GmRequest_(iAnyObject *obj) {
188 verifyDomain_TlsCertificate(cert, parts.host) ? "valid" : "not valid"); 204 verifyDomain_TlsCertificate(cert, parts.host) ? "valid" : "not valid");
189 } 205 }
190 fflush(stdout); 206 fflush(stdout);
191 }
192#endif 207#endif
208 }
193 unlock_Mutex(&d->mutex); 209 unlock_Mutex(&d->mutex);
194 iNotifyAudience(d, finished, GmRequestFinished); 210 iNotifyAudience(d, finished, GmRequestFinished);
195} 211}
@@ -211,7 +227,7 @@ void submit_GmRequest(iGmRequest *d) {
211 /* TODO: Check supported file types: images, audio */ 227 /* TODO: Check supported file types: images, audio */
212 /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ 228 /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */
213 d->code = success_GmStatusCode; 229 d->code = success_GmStatusCode;
214 d->certState = notApplicable_GmRequestCertification; 230 d->certFlags = 0;
215 if (endsWithCase_String(path, ".gmi")) { 231 if (endsWithCase_String(path, ".gmi")) {
216 setCStr_String(&d->header, "text/gemini; charset=utf-8"); 232 setCStr_String(&d->header, "text/gemini; charset=utf-8");
217 } 233 }
@@ -231,6 +247,7 @@ void submit_GmRequest(iGmRequest *d) {
231 setCStr_String(&d->header, "application/octet-stream"); 247 setCStr_String(&d->header, "application/octet-stream");
232 } 248 }
233 set_Block(&d->body, collect_Block(readAll_File(f))); 249 set_Block(&d->body, collect_Block(readAll_File(f)));
250 d->state = receivingBody_GmRequestState;
234 iNotifyAudience(d, updated, GmRequestUpdated); 251 iNotifyAudience(d, updated, GmRequestUpdated);
235 } 252 }
236 else { 253 else {
@@ -238,14 +255,14 @@ void submit_GmRequest(iGmRequest *d) {
238 setCStr_String(&d->header, cstr_String(path)); 255 setCStr_String(&d->header, cstr_String(path));
239 } 256 }
240 iRelease(f); 257 iRelease(f);
258 d->certFlags = 0;
241 d->state = finished_GmRequestState; 259 d->state = finished_GmRequestState;
242 d->certState = notApplicable_GmRequestCertification;
243 iNotifyAudience(d, finished, GmRequestFinished); 260 iNotifyAudience(d, finished, GmRequestFinished);
244 return; 261 return;
245 } 262 }
246 else if (equalCase_Rangecc(&url.protocol, "data")) { 263 else if (equalCase_Rangecc(&url.protocol, "data")) {
247 d->code = success_GmStatusCode; 264 d->code = success_GmStatusCode;
248 d->certState = notApplicable_GmRequestCertification; 265 d->certFlags = 0;
249 iString *src = collectNewCStr_String(url.protocol.start + 5); 266 iString *src = collectNewCStr_String(url.protocol.start + 5);
250 iRangecc header = { constBegin_String(src), constBegin_String(src) }; 267 iRangecc header = { constBegin_String(src), constBegin_String(src) };
251 while (header.end < constEnd_String(src) && *header.end != ',') { 268 while (header.end < constEnd_String(src) && *header.end != ',') {
@@ -269,6 +286,8 @@ void submit_GmRequest(iGmRequest *d) {
269 set_String(src, collect_String(urlDecode_String(src))); 286 set_String(src, collect_String(urlDecode_String(src)));
270 } 287 }
271 set_Block(&d->body, &src->chars); 288 set_Block(&d->body, &src->chars);
289 d->state = receivingBody_GmRequestState;
290 iNotifyAudience(d, updated, GmRequestUpdated);
272 d->state = finished_GmRequestState; 291 d->state = finished_GmRequestState;
273 iNotifyAudience(d, finished, GmRequestFinished); 292 iNotifyAudience(d, finished, GmRequestFinished);
274 return; 293 return;
@@ -314,4 +333,14 @@ const iString *url_GmRequest(const iGmRequest *d) {
314 return &d->url; 333 return &d->url;
315} 334}
316 335
336int certFlags_GmRequest(const iGmRequest *d) {
337 return d->certFlags;
338}
339
340iDate certExpirationDate_GmRequest(const iGmRequest *d) {
341 iDate expiry;
342 validUntil_TlsCertificate(serverCertificate_TlsRequest(d->req), &expiry);
343 return expiry;
344}
345
317iDefineClass(GmRequest) 346iDefineClass(GmRequest)
diff --git a/src/gmrequest.h b/src/gmrequest.h
index 37f324eb..500ed71b 100644
--- a/src/gmrequest.h
+++ b/src/gmrequest.h
@@ -1,9 +1,9 @@
1#pragma once 1#pragma once
2 2
3#include <the_Foundation/audience.h> 3#include <the_Foundation/audience.h>
4#include <the_Foundation/object.h> 4#include <the_Foundation/tlsrequest.h>
5 5
6#include "gemini.h" 6#include "gmutil.h"
7 7
8iDeclareClass(GmRequest) 8iDeclareClass(GmRequest)
9iDeclareObjectConstruction(GmRequest) 9iDeclareObjectConstruction(GmRequest)
@@ -16,11 +16,11 @@ iDeclareAudienceGetter(GmRequest, finished)
16void setUrl_GmRequest (iGmRequest *, const iString *url); 16void setUrl_GmRequest (iGmRequest *, const iString *url);
17void submit_GmRequest (iGmRequest *); 17void submit_GmRequest (iGmRequest *);
18 18
19enum iGmRequestCertification { 19enum iGmRequestCertFlags {
20 notApplicable_GmRequestCertification, 20 available_GmRequestCertFlag = iBit(1), /* certificate provided by server */
21 invalid_GmRequestCertification, 21 trusted_GmRequestCertFlag = iBit(2), /* TOFU status */
22 valid_GmRequestCertification, 22 timeVerified_GmRequestCertFlag = iBit(3), /* has not expired */
23 expired_GmRequestCertification, 23 domainVerified_GmRequestCertFlag = iBit(4), /* cert matches server domain */
24}; 24};
25 25
26iBool isFinished_GmRequest (const iGmRequest *); 26iBool isFinished_GmRequest (const iGmRequest *);
@@ -28,3 +28,6 @@ enum iGmStatusCode status_GmRequest (const iGmRequest *);
28const iString * meta_GmRequest (const iGmRequest *); 28const iString * meta_GmRequest (const iGmRequest *);
29const iBlock * body_GmRequest (const iGmRequest *); 29const iBlock * body_GmRequest (const iGmRequest *);
30const iString * url_GmRequest (const iGmRequest *); 30const iString * url_GmRequest (const iGmRequest *);
31
32int certFlags_GmRequest (const iGmRequest *);
33iDate certExpirationDate_GmRequest(const iGmRequest *);
diff --git a/src/gmutil.c b/src/gmutil.c
index bf08e960..0caadd32 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -1,5 +1,4 @@
1#include "gmutil.h" 1#include "gmutil.h"
2#include "gemini.h"
3 2
4#include <the_Foundation/regexp.h> 3#include <the_Foundation/regexp.h>
5#include <the_Foundation/object.h> 4#include <the_Foundation/object.h>
@@ -88,6 +87,12 @@ void cleanUrlPath_String(iString *d) {
88 deinit_String(&clean); 87 deinit_String(&clean);
89} 88}
90 89
90iRangecc urlHost_String(const iString *d) {
91 iUrl url;
92 init_Url(&url, d);
93 return url.host;
94}
95
91const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelative) { 96const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelative) {
92 iUrl orig; 97 iUrl orig;
93 iUrl rel; 98 iUrl rel;
diff --git a/src/gmutil.h b/src/gmutil.h
index 3342b262..6c1b01ad 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -54,5 +54,6 @@ struct Impl_Url {
54 54
55void init_Url (iUrl *, const iString *text); 55void init_Url (iUrl *, const iString *text);
56 56
57iRangecc urlHost_String (const iString *);
57const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); 58const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative);
58void urlEncodeSpaces_String (iString *); 59void urlEncodeSpaces_String (iString *);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 954ded0c..4c20a188 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1,11 +1,11 @@
1#include "documentwidget.h" 1#include "documentwidget.h"
2#include "scrollwidget.h" 2#include "scrollwidget.h"
3#include "inputwidget.h" 3#include "inputwidget.h"
4#include "labelwidget.h"
4#include "paint.h" 5#include "paint.h"
5#include "command.h" 6#include "command.h"
6#include "util.h" 7#include "util.h"
7#include "app.h" 8#include "app.h"
8#include "../gemini.h"
9#include "../gmdocument.h" 9#include "../gmdocument.h"
10#include "../gmrequest.h" 10#include "../gmrequest.h"
11#include "../gmutil.h" 11#include "../gmutil.h"
@@ -406,13 +406,37 @@ static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY) {
406 scroll_DocumentWidget_(d, 0); /* clamp it */ 406 scroll_DocumentWidget_(d, 0); /* clamp it */
407} 407}
408 408
409static void checkResponseCode_DocumentWidget_(iDocumentWidget *d) { 409static void updateTrust_DocumentWidget_(iDocumentWidget *d) {
410 iAssert(d->request);
411#define openLock_CStr "\U0001f513"
412#define closedLock_CStr "\U0001f512"
413 int certFlags = certFlags_GmRequest(d->request);
414 iLabelWidget *lock = findWidget_App("navbar.lock");
415 if (~certFlags & available_GmRequestCertFlag) {
416 setFlags_Widget(as_Widget(lock), disabled_WidgetFlag, iTrue);
417 updateTextCStr_LabelWidget(lock, gray50_ColorEscape openLock_CStr);
418 return;
419 }
420 setFlags_Widget(as_Widget(lock), disabled_WidgetFlag, iFalse);
421 if (~certFlags & domainVerified_GmRequestCertFlag) {
422 updateTextCStr_LabelWidget(lock, red_ColorEscape closedLock_CStr);
423 }
424 else if (certFlags & trusted_GmRequestCertFlag) {
425 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_CStr);
426 }
427 else {
428 updateTextCStr_LabelWidget(lock, orange_ColorEscape closedLock_CStr);
429 }
430}
431
432static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
410 if (!d->request) { 433 if (!d->request) {
411 return; 434 return;
412 } 435 }
413 enum iGmStatusCode statusCode = status_GmRequest(d->request); 436 enum iGmStatusCode statusCode = status_GmRequest(d->request);
414 if (d->state == fetching_DocumentState) { 437 if (d->state == fetching_DocumentState) {
415 d->state = receivedPartialResponse_DocumentState; 438 d->state = receivedPartialResponse_DocumentState;
439 updateTrust_DocumentWidget_(d);
416 switch (statusCode / 10) { 440 switch (statusCode / 10) {
417 case 1: /* input required */ { 441 case 1: /* input required */ {
418 iUrl parts; 442 iUrl parts;
@@ -594,14 +618,13 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
594 } 618 }
595 else if (isCommand_Widget(w, ev, "document.request.updated") && 619 else if (isCommand_Widget(w, ev, "document.request.updated") &&
596 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) { 620 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) {
597 checkResponseCode_DocumentWidget_(d); 621 checkResponse_DocumentWidget_(d);
598// updateSource_DocumentWidget_(d);
599 return iFalse; 622 return iFalse;
600 } 623 }
601 else if (isCommand_Widget(w, ev, "document.request.finished") && 624 else if (isCommand_Widget(w, ev, "document.request.finished") &&
602 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) { 625 pointerLabel_Command(command_UserEvent(ev), "request") == d->request) {
603// updateSource_DocumentWidget_(d); 626 iAssert(d->state == receivedPartialResponse_DocumentState); /* must already have been updated at least once */
604 checkResponseCode_DocumentWidget_(d); 627 checkResponse_DocumentWidget_(d);
605 d->state = ready_DocumentState; 628 d->state = ready_DocumentState;
606 iReleasePtr(&d->request); 629 iReleasePtr(&d->request);
607 postCommandf_App("document.changed url:%s", cstr_String(d->url)); 630 postCommandf_App("document.changed url:%s", cstr_String(d->url));