summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-12-02 15:07:17 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-12-02 15:07:17 +0200
commit849fd39d08bd4164ed486c2f4addc2cab7a85f9c (patch)
tree2e4c82c362c042abfe610b3d1c2bca1a2e21134e
parent9c384de6e9aa02a628b54b9930c22730f0a22dad (diff)
Show a banner warning about certificate issues
-rw-r--r--src/gmcerts.c15
-rw-r--r--src/gmcerts.h2
-rw-r--r--src/gmdocument.c19
-rw-r--r--src/gmdocument.h9
-rw-r--r--src/ui/documentwidget.c130
-rw-r--r--src/ui/text.c4
6 files changed, 137 insertions, 42 deletions
diff --git a/src/gmcerts.c b/src/gmcerts.c
index f7475348..a1df1d20 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -432,6 +432,21 @@ void setTrusted_GmCerts(iGmCerts *d, iRangecc domain, const iBlock *fingerprint,
432 unlock_Mutex(d->mtx); 432 unlock_Mutex(d->mtx);
433} 433}
434 434
435iTime domainValidUntil_GmCerts(const iGmCerts *d, iRangecc domain) {
436 iTime expiry;
437 iZap(expiry);
438 lock_Mutex(d->mtx);
439 iString key;
440 initRange_String(&key, domain);
441 const iTrustEntry *trust = constValue_StringHash(d->trusted, &key);
442 if (trust) {
443 expiry = trust->validUntil;
444 }
445 deinit_String(&key);
446 unlock_Mutex(d->mtx);
447 return expiry;
448}
449
435iGmIdentity *identity_GmCerts(iGmCerts *d, unsigned int id) { 450iGmIdentity *identity_GmCerts(iGmCerts *d, unsigned int id) {
436 return at_PtrArray(&d->idents, id); 451 return at_PtrArray(&d->idents, id);
437} 452}
diff --git a/src/gmcerts.h b/src/gmcerts.h
index 2fd1023a..a28c44b4 100644
--- a/src/gmcerts.h
+++ b/src/gmcerts.h
@@ -62,6 +62,8 @@ typedef iBool (*iGmCertsIdentityFilterFunc)(void *context, const iGmIdentity *);
62iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const iTlsCertificate *cert); 62iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const iTlsCertificate *cert);
63void setTrusted_GmCerts (iGmCerts *, iRangecc domain, const iBlock *fingerprint, 63void setTrusted_GmCerts (iGmCerts *, iRangecc domain, const iBlock *fingerprint,
64 const iDate *validUntil); 64 const iDate *validUntil);
65iTime domainValidUntil_GmCerts(const iGmCerts *, iRangecc domain);
66
65/** 67/**
66 * Create a new self-signed TLS client certificate for identifying the user. 68 * Create a new self-signed TLS client certificate for identifying the user.
67 * @a commonName and the other name parameters are inserted in the subject field 69 * @a commonName and the other name parameters are inserted in the subject field
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 9e78c2e0..d01ba0b9 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -64,10 +64,10 @@ struct Impl_GmDocument {
64 iString source; 64 iString source;
65 iString url; /* for resolving relative links */ 65 iString url; /* for resolving relative links */
66 iString localHost; 66 iString localHost;
67 iBool siteBannerEnabled;
68 iInt2 size; 67 iInt2 size;
69 iArray layout; /* contents of source, laid out in document space */ 68 iArray layout; /* contents of source, laid out in document space */
70 iPtrArray links; 69 iPtrArray links;
70 enum iGmDocumentBanner bannerType;
71 iString bannerText; 71 iString bannerText;
72 iString title; /* the first top-level title */ 72 iString title; /* the first top-level title */
73 iArray headings; 73 iArray headings;
@@ -310,7 +310,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
310 int preFont = preformatted_FontId; 310 int preFont = preformatted_FontId;
311 uint16_t preId = 0; 311 uint16_t preId = 0;
312 iBool enableIndents = iFalse; 312 iBool enableIndents = iFalse;
313 iBool addSiteBanner = d->siteBannerEnabled; 313 iBool addSiteBanner = d->bannerType != none_GmDocumentBanner;
314 enum iGmLineType prevType = text_GmLineType; 314 enum iGmLineType prevType = text_GmLineType;
315 if (d->format == plainText_GmDocumentFormat) { 315 if (d->format == plainText_GmDocumentFormat) {
316 isPreformat = iTrue; 316 isPreformat = iTrue;
@@ -383,6 +383,10 @@ static void doLayout_GmDocument_(iGmDocument *d) {
383 iGmRun banner = { .flags = decoration_GmRunFlag | siteBanner_GmRunFlag }; 383 iGmRun banner = { .flags = decoration_GmRunFlag | siteBanner_GmRunFlag };
384 banner.bounds = zero_Rect(); 384 banner.bounds = zero_Rect();
385 banner.visBounds = init_Rect(0, 0, d->size.x, lineHeight_Text(banner_FontId) * 2); 385 banner.visBounds = init_Rect(0, 0, d->size.x, lineHeight_Text(banner_FontId) * 2);
386 if (d->bannerType == certificateWarning_GmDocumentBanner) {
387 banner.visBounds.size.y += iMaxi(6000 * lineHeight_Text(uiLabel_FontId) /
388 d->size.x, lineHeight_Text(uiLabel_FontId) * 5);
389 }
386 banner.font = banner_FontId; 390 banner.font = banner_FontId;
387 banner.text = bannerText; 391 banner.text = bannerText;
388 banner.color = tmBannerTitle_ColorId; 392 banner.color = tmBannerTitle_ColorId;
@@ -620,7 +624,7 @@ void init_GmDocument(iGmDocument *d) {
620 init_String(&d->source); 624 init_String(&d->source);
621 init_String(&d->url); 625 init_String(&d->url);
622 init_String(&d->localHost); 626 init_String(&d->localHost);
623 d->siteBannerEnabled = iTrue; 627 d->bannerType = siteDomain_GmDocumentBanner;
624 d->size = zero_I2(); 628 d->size = zero_I2();
625 init_Array(&d->layout, sizeof(iGmRun)); 629 init_Array(&d->layout, sizeof(iGmRun));
626 init_PtrArray(&d->links); 630 init_PtrArray(&d->links);
@@ -661,7 +665,6 @@ void reset_GmDocument(iGmDocument *d) {
661 clear_String(&d->url); 665 clear_String(&d->url);
662 clear_String(&d->localHost); 666 clear_String(&d->localHost);
663 d->themeSeed = 0; 667 d->themeSeed = 0;
664 d->siteBannerEnabled = iTrue;
665} 668}
666 669
667static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { 670static void setDerivedThemeColors_(enum iGmDocumentTheme theme) {
@@ -1085,8 +1088,8 @@ void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) {
1085 d->format = format; 1088 d->format = format;
1086} 1089}
1087 1090
1088void setSiteBannerEnabled_GmDocument(iGmDocument *d, iBool siteBannerEnabled) { 1091void setBanner_GmDocument(iGmDocument *d, enum iGmDocumentBanner type) {
1089 d->siteBannerEnabled = siteBannerEnabled; 1092 d->bannerType = type;
1090} 1093}
1091 1094
1092void setWidth_GmDocument(iGmDocument *d, int width) { 1095void setWidth_GmDocument(iGmDocument *d, int width) {
@@ -1203,6 +1206,10 @@ iInt2 size_GmDocument(const iGmDocument *d) {
1203 return d->size; 1206 return d->size;
1204} 1207}
1205 1208
1209enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *d) {
1210 return d->bannerType;
1211}
1212
1206iBool hasSiteBanner_GmDocument(const iGmDocument *d) { 1213iBool hasSiteBanner_GmDocument(const iGmDocument *d) {
1207 return siteBanner_GmDocument(d) != NULL; 1214 return siteBanner_GmDocument(d) != NULL;
1208} 1215}
diff --git a/src/gmdocument.h b/src/gmdocument.h
index c6f64e1d..b1121d85 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -115,9 +115,15 @@ enum iGmDocumentFormat {
115 plainText_GmDocumentFormat, 115 plainText_GmDocumentFormat,
116}; 116};
117 117
118enum iGmDocumentBanner {
119 none_GmDocumentBanner,
120 siteDomain_GmDocumentBanner,
121 certificateWarning_GmDocumentBanner,
122};
123
118void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); 124void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed);
119void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); 125void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format);
120void setSiteBannerEnabled_GmDocument(iGmDocument *, iBool siteBannerEnabled); 126void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type);
121void setWidth_GmDocument (iGmDocument *, int width); 127void setWidth_GmDocument (iGmDocument *, int width);
122void redoLayout_GmDocument (iGmDocument *); 128void redoLayout_GmDocument (iGmDocument *);
123void setUrl_GmDocument (iGmDocument *, const iString *url); 129void setUrl_GmDocument (iGmDocument *, const iString *url);
@@ -135,6 +141,7 @@ void render_GmDocument (const iGmDocument *, iRangei visRan
135iInt2 size_GmDocument (const iGmDocument *); 141iInt2 size_GmDocument (const iGmDocument *);
136const iGmRun * siteBanner_GmDocument (const iGmDocument *); 142const iGmRun * siteBanner_GmDocument (const iGmDocument *);
137iBool hasSiteBanner_GmDocument (const iGmDocument *); 143iBool hasSiteBanner_GmDocument (const iGmDocument *);
144enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *);
138const iString * bannerText_GmDocument (const iGmDocument *); 145const iString * bannerText_GmDocument (const iGmDocument *);
139const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ 146const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */
140const iString * source_GmDocument (const iGmDocument *); 147const iString * source_GmDocument (const iGmDocument *);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 3f952411..432dd290 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -754,6 +754,16 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) {
754 updateTimestampBuf_DocumentWidget_(d); 754 updateTimestampBuf_DocumentWidget_(d);
755} 755}
756 756
757static enum iGmDocumentBanner bannerType_DocumentWidget_(const iDocumentWidget *d) {
758 if (d->certFlags & available_GmCertFlag) {
759 const int req = domainVerified_GmCertFlag | timeVerified_GmCertFlag | trusted_GmCertFlag;
760 if ((d->certFlags & req) != req) {
761 return certificateWarning_GmDocumentBanner;
762 }
763 }
764 return siteDomain_GmDocumentBanner;
765}
766
757static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, 767static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code,
758 const iString *meta) { 768 const iString *meta) {
759 iString *src = collectNewCStr_String("# "); 769 iString *src = collectNewCStr_String("# ");
@@ -794,7 +804,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
794 break; 804 break;
795 } 805 }
796 } 806 }
797 setSiteBannerEnabled_GmDocument(d->doc, useBanner); 807 setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner);
798 setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); 808 setFormat_GmDocument(d->doc, gemini_GmDocumentFormat);
799 setSource_DocumentWidget_(d, src); 809 setSource_DocumentWidget_(d, src);
800 updateTheme_DocumentWidget_(d); 810 updateTheme_DocumentWidget_(d);
@@ -956,15 +966,18 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r
956 return; 966 return;
957 } 967 }
958 setFlags_Widget(as_Widget(lock), disabled_WidgetFlag, iFalse); 968 setFlags_Widget(as_Widget(lock), disabled_WidgetFlag, iFalse);
969 const iBool isDarkMode = isDark_ColorTheme(colorTheme_App());
959 if (~d->certFlags & domainVerified_GmCertFlag) { 970 if (~d->certFlags & domainVerified_GmCertFlag) {
960 updateTextCStr_LabelWidget(lock, red_ColorEscape closedLock_CStr); 971 updateTextCStr_LabelWidget(lock, red_ColorEscape openLock_CStr);
961 } 972 }
962 else if (d->certFlags & trusted_GmCertFlag) { 973 else if (d->certFlags & trusted_GmCertFlag) {
963 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_CStr); 974 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_CStr);
964 } 975 }
965 else { 976 else {
966 updateTextCStr_LabelWidget(lock, orange_ColorEscape closedLock_CStr); 977 updateTextCStr_LabelWidget(lock, isDarkMode ? orange_ColorEscape closedLock_CStr
978 : black_ColorEscape closedLock_CStr);
967 } 979 }
980 setBanner_GmDocument(d->doc, bannerType_DocumentWidget_(d));
968} 981}
969 982
970static void parseUser_DocumentWidget_(iDocumentWidget *d) { 983static void parseUser_DocumentWidget_(iDocumentWidget *d) {
@@ -1490,6 +1503,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1490 } 1503 }
1491 else if (equal_Command(cmd, "theme.changed") && document_App() == d) { 1504 else if (equal_Command(cmd, "theme.changed") && document_App() == d) {
1492 updateTheme_DocumentWidget_(d); 1505 updateTheme_DocumentWidget_(d);
1506 updateTrust_DocumentWidget_(d, NULL);
1493 updateSideIconBuf_DocumentWidget_(d); 1507 updateSideIconBuf_DocumentWidget_(d);
1494 invalidate_DocumentWidget_(d); 1508 invalidate_DocumentWidget_(d);
1495 refresh_Widget(w); 1509 refresh_Widget(w);
@@ -2507,6 +2521,83 @@ static void drawMark_DrawContext_(void *context, const iGmRun *run) {
2507 } 2521 }
2508} 2522}
2509 2523
2524static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 visPos) {
2525 const iGmDocument *doc = d->widget->doc;
2526 const iChar icon = siteIcon_GmDocument(doc);
2527 iString str;
2528 init_String(&str);
2529 iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2));
2530 if (icon) {
2531 appendChar_String(&str, icon);
2532 const iRect iconRect = visualBounds_Text(run->font, range_String(&str));
2533 drawRange_Text(
2534 run->font,
2535 addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2),
2536 tmBannerIcon_ColorId,
2537 range_String(&str));
2538 bpos.x += right_Rect(iconRect) + 3 * gap_Text;
2539 }
2540 drawRange_Text(run->font,
2541 bpos,
2542 tmBannerTitle_ColorId,
2543 bannerText_DocumentWidget_(d->widget));
2544 if (bannerType_GmDocument(doc) == certificateWarning_GmDocumentBanner) {
2545 const int domainHeight = lineHeight_Text(banner_FontId) * 2;
2546 iRect rect = { add_I2(visPos, init_I2(0, domainHeight)),
2547 addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) };
2548 format_String(&str, "UNTRUSTED CERTIFICATE");
2549 const int certFlags = d->widget->certFlags;
2550 if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) {
2551 iUrl parts;
2552 init_Url(&parts, d->widget->mod.url);
2553 const iTime oldUntil = domainValidUntil_GmCerts(certs_App(), parts.host);
2554 iDate exp;
2555 init_Date(&exp, &oldUntil);
2556 iTime now;
2557 initCurrent_Time(&now);
2558 const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24;
2559 if (days <= 30) {
2560 appendFormat_String(&str,
2561 "\nThe received certificate may have been recently renewed \u2014 it is "
2562 "for the correct domain and has not expired. The currently trusted "
2563 "certificate will expire on %s, in %d days.",
2564 cstrCollect_String(format_Date(&exp, "%Y-%m-%d")),
2565 days);
2566 }
2567 else {
2568 appendFormat_String(&str, "\nThe received certificate is valid but different than "
2569 "the one we trust.");
2570 }
2571 }
2572 else if (certFlags & domainVerified_GmCertFlag) {
2573 appendFormat_String(&str, "\nThe received certificate has expired on %s.",
2574 cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d")));
2575 }
2576 if (certFlags & haveFingerprint_GmCertFlag) {
2577
2578 }
2579 const iInt2 dims = advanceWrapRange_Text(
2580 uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str));
2581 fillRect_Paint(&d->paint,
2582 init_Rect(0,
2583 visPos.y + domainHeight,
2584 d->widgetBounds.size.x,
2585 dims.y + lineHeight_Text(uiContent_FontId) / 2),
2586 orange_ColorId);
2587 const int fg = black_ColorId;
2588 bpos = topLeft_Rect(rect);
2589 draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0");
2590 adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI);
2591 drawWrapRange_Text(uiContent_FontId,
2592 addY_I2(topLeft_Rect(rect), (lineHeight_Text(uiLabelLarge_FontId) -
2593 lineHeight_Text(uiContent_FontId)) / 2),
2594 width_Rect(rect),
2595 fg,
2596 range_String(&str));
2597 }
2598 deinit_String(&str);
2599}
2600
2510static void drawRun_DrawContext_(void *context, const iGmRun *run) { 2601static void drawRun_DrawContext_(void *context, const iGmRun *run) {
2511 iDrawContext *d = context; 2602 iDrawContext *d = context;
2512 const iInt2 origin = d->viewPos; 2603 const iInt2 origin = d->viewPos;
@@ -2540,41 +2631,14 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
2540 } 2631 }
2541 } 2632 }
2542 if (run->flags & siteBanner_GmRunFlag) { 2633 if (run->flags & siteBanner_GmRunFlag) {
2543 /* Draw the site banner. */ 2634 /* Banner background. */
2544 fillRect_Paint( 2635 fillRect_Paint(
2545 &d->paint, 2636 &d->paint,
2546 initCorners_Rect(topLeft_Rect(d->widgetBounds), 2637 initCorners_Rect(topLeft_Rect(d->widgetBounds),
2547 init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), 2638 init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))),
2548 visPos.y + height_Rect(run->visBounds))), 2639 visPos.y + height_Rect(run->visBounds))),
2549 tmBannerBackground_ColorId); 2640 tmBannerBackground_ColorId);
2550 const iChar icon = siteIcon_GmDocument(doc); 2641 drawBannerRun_DrawContext_(d, run, visPos);
2551 iString bannerText;
2552 init_String(&bannerText);
2553 iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2));
2554 if (icon) {
2555// appendChar_String(&bannerText, 0x2b24); // icon);
2556// const iRect iconRect = visualBounds_Text(hugeBold_FontId, range_String(&bannerText));
2557// drawRange_Text(hugeBold_FontId, /*run->font,*/
2558// addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2),
2559// tmBannerIcon_ColorId,
2560// range_String(&bannerText));
2561// clear_String(&bannerText);
2562 appendChar_String(&bannerText, icon);
2563 const iRect iconRect = visualBounds_Text(run->font, range_String(&bannerText));
2564 drawRange_Text(
2565 run->font,
2566 addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2),
2567 tmBannerIcon_ColorId,
2568 range_String(&bannerText));
2569 bpos.x += right_Rect(iconRect) + 3 * gap_Text;
2570 }
2571 drawRange_Text(run->font,
2572 bpos,
2573 tmBannerTitle_ColorId,
2574 bannerText_DocumentWidget_(d->widget));
2575// isEmpty_String(d->widget->titleUser) ? run->text
2576// : range_String(d->widget->titleUser));
2577 deinit_String(&bannerText);
2578 } 2642 }
2579 else { 2643 else {
2580 if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { 2644 if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) {
diff --git a/src/ui/text.c b/src/ui/text.c
index 3a9b6983..a8be1778 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -919,10 +919,10 @@ iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) {
919int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc text) { 919int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc text) {
920 const char *endp; 920 const char *endp;
921 while (!isEmpty_Range(&text)) { 921 while (!isEmpty_Range(&text)) {
922 tryAdvance_Text(fontId, text, maxWidth, &endp); 922 const iInt2 adv = tryAdvance_Text(fontId, text, maxWidth, &endp);
923 drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endp }); 923 drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endp });
924 text.start = endp; 924 text.start = endp;
925 pos.y += lineHeight_Text(fontId); 925 pos.y += adv.y;
926 } 926 }
927 return pos.y; 927 return pos.y;
928} 928}