diff options
-rw-r--r-- | src/app.c | 8 | ||||
-rw-r--r-- | src/gmcerts.c | 17 | ||||
-rw-r--r-- | src/gmcerts.h | 14 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 62 | ||||
-rw-r--r-- | src/ui/util.c | 8 | ||||
-rw-r--r-- | src/ui/widget.c | 2 | ||||
-rw-r--r-- | src/ui/window.c | 17 |
7 files changed, 105 insertions, 23 deletions
@@ -537,7 +537,8 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf) { | |||
537 | } | 537 | } |
538 | 538 | ||
539 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { | 539 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { |
540 | if (equal_Command(cmd, "ident.accept") || equal_Command(cmd, "cancel")) { | 540 | iApp *d = &app_; |
541 | if (equal_Command(cmd, "ident.accept") || equal_Command(cmd, "cancel")) { | ||
541 | if (equal_Command(cmd, "ident.accept")) { | 542 | if (equal_Command(cmd, "ident.accept")) { |
542 | const iString *commonName = text_InputWidget (findChild_Widget(dlg, "ident.common")); | 543 | const iString *commonName = text_InputWidget (findChild_Widget(dlg, "ident.common")); |
543 | const iString *userId = text_InputWidget (findChild_Widget(dlg, "ident.userid")); | 544 | const iString *userId = text_InputWidget (findChild_Widget(dlg, "ident.userid")); |
@@ -584,7 +585,10 @@ static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { | |||
584 | } | 585 | } |
585 | } | 586 | } |
586 | } | 587 | } |
587 | 588 | /* The input seems fine. */ | |
589 | newIdentity_GmCerts(d->certs, isTemp ? temporary_GmIdentityFlag : 0, | ||
590 | until, commonName, userId, organization, country); | ||
591 | postCommand_App("idents.changed"); | ||
588 | } | 592 | } |
589 | destroy_Widget(dlg); | 593 | destroy_Widget(dlg); |
590 | return iTrue; | 594 | return iTrue; |
diff --git a/src/gmcerts.c b/src/gmcerts.c index e13e9a8d..2624eb4d 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c | |||
@@ -125,6 +125,7 @@ static iBool isValid_GmIdentity_(const iGmIdentity *d) { | |||
125 | static void setCertificate_GmIdentity_(iGmIdentity *d, iTlsCertificate *cert) { | 125 | static void setCertificate_GmIdentity_(iGmIdentity *d, iTlsCertificate *cert) { |
126 | delete_TlsCertificate(d->cert); | 126 | delete_TlsCertificate(d->cert); |
127 | d->cert = cert; | 127 | d->cert = cert; |
128 | set_Block(&d->fingerprint, collect_Block(fingerprint_TlsCertificate(cert))); | ||
128 | } | 129 | } |
129 | 130 | ||
130 | static const iString *readFile_(const iString *path) { | 131 | static const iString *readFile_(const iString *path) { |
@@ -249,7 +250,6 @@ static void loadIdentityFromCertificate_GmCerts_(iGmCerts *d, const iString *crt | |||
249 | iGmIdentity *ident = findIdentity_GmCerts_(d, finger); | 250 | iGmIdentity *ident = findIdentity_GmCerts_(d, finger); |
250 | if (!ident) { | 251 | if (!ident) { |
251 | ident = new_GmIdentity(); | 252 | ident = new_GmIdentity(); |
252 | set_Block(&ident->fingerprint, finger); | ||
253 | iDate today; | 253 | iDate today; |
254 | initCurrent_Date(&today); | 254 | initCurrent_Date(&today); |
255 | set_String(&ident->notes, collect_String(format_Date(&today, "Imported on %b %d, %Y"))); | 255 | set_String(&ident->notes, collect_String(format_Date(&today, "Imported on %b %d, %Y"))); |
@@ -387,9 +387,9 @@ iGmIdentity *newIdentity_GmCerts(iGmCerts *d, int flags, iDate validUntil, const | |||
387 | const iTlsCertificateName names[] = { | 387 | const iTlsCertificateName names[] = { |
388 | { issuerCommonName_TlsCertificateNameType, collectNewCStr_String("fi.skyjake.Lagrange") }, | 388 | { issuerCommonName_TlsCertificateNameType, collectNewCStr_String("fi.skyjake.Lagrange") }, |
389 | { subjectCommonName_TlsCertificateNameType, commonName }, | 389 | { subjectCommonName_TlsCertificateNameType, commonName }, |
390 | { subjectUserId_TlsCertificateNameType, userId }, | 390 | { subjectUserId_TlsCertificateNameType, !isEmpty_String(userId) ? userId : NULL }, |
391 | { subjectOrganization_TlsCertificateNameType, org }, | 391 | { subjectOrganization_TlsCertificateNameType, !isEmpty_String(org) ? org : NULL }, |
392 | { subjectCountry_TlsCertificateNameType, country }, | 392 | { subjectCountry_TlsCertificateNameType, !isEmpty_String(country) ? country : NULL }, |
393 | { 0, NULL } | 393 | { 0, NULL } |
394 | }; | 394 | }; |
395 | iGmIdentity *id = new_GmIdentity(); | 395 | iGmIdentity *id = new_GmIdentity(); |
@@ -398,17 +398,22 @@ iGmIdentity *newIdentity_GmCerts(iGmCerts *d, int flags, iDate validUntil, const | |||
398 | if (~flags & temporary_GmIdentityFlag) { | 398 | if (~flags & temporary_GmIdentityFlag) { |
399 | const char *finger = cstrCollect_String(hexEncode_Block(&id->fingerprint)); | 399 | const char *finger = cstrCollect_String(hexEncode_Block(&id->fingerprint)); |
400 | if (!writeTextFile_( | 400 | if (!writeTextFile_( |
401 | collect_String(concatCStr_Path(&d->saveDir, format_CStr("%s.crt", finger))), | 401 | collect_String(concatCStr_Path(&d->saveDir, format_CStr("idents/%s.crt", finger))), |
402 | collect_String(pem_TlsCertificate(id->cert)))) { | 402 | collect_String(pem_TlsCertificate(id->cert)))) { |
403 | delete_GmIdentity(id); | 403 | delete_GmIdentity(id); |
404 | return NULL; | 404 | return NULL; |
405 | } | 405 | } |
406 | if (!writeTextFile_( | 406 | if (!writeTextFile_( |
407 | collect_String(concatCStr_Path(&d->saveDir, format_CStr("%s.key", finger))), | 407 | collect_String(concatCStr_Path(&d->saveDir, format_CStr("idents/%s.key", finger))), |
408 | collect_String(privateKeyPem_TlsCertificate(id->cert)))) { | 408 | collect_String(privateKeyPem_TlsCertificate(id->cert)))) { |
409 | delete_GmIdentity(id); | 409 | delete_GmIdentity(id); |
410 | return NULL; | 410 | return NULL; |
411 | } | 411 | } |
412 | } | 412 | } |
413 | pushBack_PtrArray(&d->idents, id); | ||
413 | return id; | 414 | return id; |
414 | } | 415 | } |
416 | |||
417 | const iPtrArray *identities_GmCerts(const iGmCerts *d) { | ||
418 | return &d->idents; | ||
419 | } | ||
diff --git a/src/gmcerts.h b/src/gmcerts.h index 6f34e5dd..32a383be 100644 --- a/src/gmcerts.h +++ b/src/gmcerts.h | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include <the_Foundation/ptrarray.h> | ||
25 | #include <the_Foundation/tlsrequest.h> | 26 | #include <the_Foundation/tlsrequest.h> |
26 | 27 | ||
27 | iDeclareType(GmIdentity) | 28 | iDeclareType(GmIdentity) |
@@ -49,6 +50,19 @@ iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const | |||
49 | 50 | ||
50 | const iGmIdentity * identityForUrl_GmCerts (const iGmCerts *, const iString *url); | 51 | const iGmIdentity * identityForUrl_GmCerts (const iGmCerts *, const iString *url); |
51 | 52 | ||
53 | /** | ||
54 | * Create a new self-signed TLS client certificate for identifying the user. | ||
55 | * @a commonName and the other name parameters are inserted in the subject field | ||
56 | * of the certificate. | ||
57 | * | ||
58 | * @param flags Identity flags. A temporary identity is not saved persistently and | ||
59 | * will be erased when the application is shut down. | ||
60 | * @param validUntil Expiration date. Must be in the future. | ||
61 | * | ||
62 | * @returns Created identity. GmCerts retains ownership of returned object. | ||
63 | */ | ||
52 | iGmIdentity * newIdentity_GmCerts (iGmCerts *, int flags, iDate validUntil, | 64 | iGmIdentity * newIdentity_GmCerts (iGmCerts *, int flags, iDate validUntil, |
53 | const iString *commonName, const iString *userId, | 65 | const iString *commonName, const iString *userId, |
54 | const iString *org, const iString *country); | 66 | const iString *org, const iString *country); |
67 | |||
68 | const iPtrArray * identities_GmCerts (const iGmCerts *); | ||
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 82efb04e..aa2e185d 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -22,11 +22,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "sidebarwidget.h" | 23 | #include "sidebarwidget.h" |
24 | 24 | ||
25 | #include "../gmdocument.h" | ||
26 | #include "app.h" | 25 | #include "app.h" |
27 | #include "bookmarks.h" | 26 | #include "bookmarks.h" |
28 | #include "command.h" | 27 | #include "command.h" |
29 | #include "documentwidget.h" | 28 | #include "documentwidget.h" |
29 | #include "gmcerts.h" | ||
30 | #include "gmdocument.h" | ||
30 | #include "inputwidget.h" | 31 | #include "inputwidget.h" |
31 | #include "labelwidget.h" | 32 | #include "labelwidget.h" |
32 | #include "paint.h" | 33 | #include "paint.h" |
@@ -216,6 +217,33 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
216 | }, 6); | 217 | }, 6); |
217 | break; | 218 | break; |
218 | } | 219 | } |
220 | case identities_SidebarMode: { | ||
221 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { | ||
222 | const iGmIdentity *ident = i.ptr; | ||
223 | /* icon, common name, used URLs, expiration, isTemp, free-form notes */ | ||
224 | iSidebarItem item; | ||
225 | init_SidebarItem(&item); | ||
226 | item.icon = ident->icon; | ||
227 | set_String(&item.label, collect_String(subject_TlsCertificate(ident->cert))); | ||
228 | iDate until; | ||
229 | validUntil_TlsCertificate(ident->cert, &until); | ||
230 | set_String( | ||
231 | &item.meta, | ||
232 | collectNewFormat_String( | ||
233 | "%s \u2014 %s\n%s", | ||
234 | isEmpty_SortedArray(&ident->useUrls) | ||
235 | ? "Not used" | ||
236 | : format_CStr("Used on %zu URLs", size_SortedArray(&ident->useUrls)), | ||
237 | ident->flags & temporary_GmIdentityFlag | ||
238 | ? "Temporary" | ||
239 | : cstrCollect_String(format_Date(&until, "Expires %b %d, %Y")), | ||
240 | cstr_String(&ident->notes))); | ||
241 | pushBack_Array(&d->items, &item); | ||
242 | } | ||
243 | /* menu: set icon, edit notes, view urls, reveal files, delete, activate on this page, | ||
244 | * deactivate on this page, deactivate everywhere */ | ||
245 | break; | ||
246 | } | ||
219 | default: | 247 | default: |
220 | break; | 248 | break; |
221 | } | 249 | } |
@@ -247,7 +275,7 @@ void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | |||
247 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | 275 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { |
248 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | 276 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); |
249 | } | 277 | } |
250 | const float heights[max_SidebarMode] = { 1.333f, 1.333f, 2.5f, 1.2f }; | 278 | const float heights[max_SidebarMode] = { 1.333f, 1.333f, 3.0f, 1.2f }; |
251 | d->itemHeight = heights[mode] * lineHeight_Text(uiContent_FontId); | 279 | d->itemHeight = heights[mode] * lineHeight_Text(uiContent_FontId); |
252 | invalidate_SidebarWidget_(d); | 280 | invalidate_SidebarWidget_(d); |
253 | /* Restore previous scroll position. */ | 281 | /* Restore previous scroll position. */ |
@@ -529,7 +557,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
529 | } | 557 | } |
530 | return iTrue; | 558 | return iTrue; |
531 | } | 559 | } |
532 | else if (equal_Command(cmd, "bookmarks.changed")) { | 560 | else if (equal_Command(cmd, "bookmarks.changed") && d->mode == bookmarks_SidebarMode) { |
561 | updateItems_SidebarWidget_(d); | ||
562 | } | ||
563 | else if (equal_Command(cmd, "idents.changed") && d->mode == identities_SidebarMode) { | ||
533 | updateItems_SidebarWidget_(d); | 564 | updateItems_SidebarWidget_(d); |
534 | } | 565 | } |
535 | else if (equal_Command(cmd, "history.delete")) { | 566 | else if (equal_Command(cmd, "history.delete")) { |
@@ -661,6 +692,9 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
661 | const iSidebarItem *item = constAt_Array(&d->items, i); | 692 | const iSidebarItem *item = constAt_Array(&d->items, i); |
662 | const iRect itemRect = { pos, init_I2(width_Rect(bufBounds), d->itemHeight) }; | 693 | const iRect itemRect = { pos, init_I2(width_Rect(bufBounds), d->itemHeight) }; |
663 | const iBool isHover = (d->hoverItem == i); | 694 | const iBool isHover = (d->hoverItem == i); |
695 | const int iconColor = | ||
696 | isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) | ||
697 | : uiIcon_ColorId; | ||
664 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); | 698 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); |
665 | if (isHover && !item->isSeparator) { | 699 | if (isHover && !item->isSeparator) { |
666 | fillRect_Paint(&p, | 700 | fillRect_Paint(&p, |
@@ -691,8 +725,7 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
691 | font, | 725 | font, |
692 | iconArea, | 726 | iconArea, |
693 | iTrue, | 727 | iTrue, |
694 | isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) | 728 | iconColor, |
695 | : uiIcon_ColorId, | ||
696 | "%s", | 729 | "%s", |
697 | cstr_String(&str)); | 730 | cstr_String(&str)); |
698 | deinit_String(&str); | 731 | deinit_String(&str); |
@@ -737,6 +770,25 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
737 | } | 770 | } |
738 | iEndCollect(); | 771 | iEndCollect(); |
739 | } | 772 | } |
773 | else if (d->mode == identities_SidebarMode) { | ||
774 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId | ||
775 | : uiTextFramelessHover_ColorId) | ||
776 | : uiText_ColorId; | ||
777 | iString icon; | ||
778 | initUnicodeN_String(&icon, &item->icon, 1); | ||
779 | drawRange_Text(font, add_I2(topLeft_Rect(itemRect), init_I2(3 * gap_UI, 0)), | ||
780 | iconColor, range_String(&icon)); | ||
781 | deinit_String(&icon); | ||
782 | drawRange_Text(font, add_I2(topLeft_Rect(itemRect), init_I2(10 * gap_UI, 0)), | ||
783 | fg, range_String(&item->label)); | ||
784 | drawRange_Text( | ||
785 | font, | ||
786 | add_I2(topLeft_Rect(itemRect), init_I2(3 * gap_UI, lineHeight_Text(font))), | ||
787 | isHover | ||
788 | ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
789 | : uiAnnotation_ColorId, | ||
790 | range_String(&item->meta)); | ||
791 | } | ||
740 | unsetClip_Paint(&p); | 792 | unsetClip_Paint(&p); |
741 | pos.y += d->itemHeight; | 793 | pos.y += d->itemHeight; |
742 | } | 794 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index b3178e34..e6067ab9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -689,9 +689,11 @@ iWidget *makeQuestion_Widget(const char *title, | |||
689 | } | 689 | } |
690 | 690 | ||
691 | void setToggle_Widget(iWidget *d, iBool active) { | 691 | void setToggle_Widget(iWidget *d, iBool active) { |
692 | setFlags_Widget(d, selected_WidgetFlag, active); | 692 | if (d) { |
693 | updateText_LabelWidget((iLabelWidget *) d, | 693 | setFlags_Widget(d, selected_WidgetFlag, active); |
694 | collectNewFormat_String("%s", isSelected_Widget(d) ? "YES" : "NO")); | 694 | updateText_LabelWidget((iLabelWidget *) d, |
695 | collectNewFormat_String("%s", isSelected_Widget(d) ? "YES" : "NO")); | ||
696 | } | ||
695 | } | 697 | } |
696 | 698 | ||
697 | static iBool toggleHandler_(iWidget *d, const char *cmd) { | 699 | static iBool toggleHandler_(iWidget *d, const char *cmd) { |
diff --git a/src/ui/widget.c b/src/ui/widget.c index f187bb82..b5ea3b0f 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -637,7 +637,7 @@ iBool isHover_Widget(const iWidget *d) { | |||
637 | } | 637 | } |
638 | 638 | ||
639 | iBool isSelected_Widget(const iWidget *d) { | 639 | iBool isSelected_Widget(const iWidget *d) { |
640 | return (d->flags & selected_WidgetFlag) != 0; | 640 | return d && (d->flags & selected_WidgetFlag) != 0; |
641 | } | 641 | } |
642 | 642 | ||
643 | iBool equalWidget_Command(const char *cmd, const iWidget *widget, const char *checkCommand) { | 643 | iBool equalWidget_Command(const char *cmd, const iWidget *widget, const char *checkCommand) { |
diff --git a/src/ui/window.c b/src/ui/window.c index fad87f08..709fc0e0 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -102,6 +102,8 @@ static const iMenuItem navMenuItems[] = { | |||
102 | { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, | 102 | { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, |
103 | { "---", 0, 0, NULL }, | 103 | { "---", 0, 0, NULL }, |
104 | { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, | 104 | { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, |
105 | { "Help", 0, 0, "!open url:about:help" }, | ||
106 | { "Release Notes", 0, 0, "!open url:about:version" }, | ||
105 | { "---", 0, 0, NULL }, | 107 | { "---", 0, 0, NULL }, |
106 | { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" } | 108 | { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" } |
107 | }; | 109 | }; |
@@ -121,10 +123,6 @@ static const iMenuItem editMenuItems[] = { | |||
121 | { "Bookmark This Page...", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 123 | { "Bookmark This Page...", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
122 | }; | 124 | }; |
123 | 125 | ||
124 | static const iMenuItem identityMenuItems[] = { | ||
125 | { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | ||
126 | }; | ||
127 | |||
128 | static const iMenuItem viewMenuItems[] = { | 126 | static const iMenuItem viewMenuItems[] = { |
129 | { "Show Bookmarks", '1', KMOD_PRIMARY, "sidebar.mode arg:0 show:1" }, | 127 | { "Show Bookmarks", '1', KMOD_PRIMARY, "sidebar.mode arg:0 show:1" }, |
130 | { "Show History", '2', KMOD_PRIMARY, "sidebar.mode arg:1 show:1" }, | 128 | { "Show History", '2', KMOD_PRIMARY, "sidebar.mode arg:1 show:1" }, |
@@ -147,6 +145,10 @@ static const iMenuItem helpMenuItems[] = { | |||
147 | }; | 145 | }; |
148 | #endif | 146 | #endif |
149 | 147 | ||
148 | static const iMenuItem identityMenuItems[] = { | ||
149 | { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | ||
150 | }; | ||
151 | |||
150 | static const char *reloadCStr_ = "\U0001f503"; | 152 | static const char *reloadCStr_ = "\U0001f503"; |
151 | static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310"; | 153 | static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310"; |
152 | 154 | ||
@@ -312,8 +314,11 @@ static void setupUserInterface_Window(iWindow *d) { | |||
312 | setId_Widget( | 314 | setId_Widget( |
313 | addChild_Widget(navBar, iClob(newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"))), | 315 | addChild_Widget(navBar, iClob(newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"))), |
314 | "reload"); | 316 | "reload"); |
315 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f464", 0, 0, "cert.client"))); | 317 | iLabelWidget *idMenu = |
316 | 318 | makeMenuButton_LabelWidget("\U0001f464", identityMenuItems, iElemCount(identityMenuItems)); | |
319 | setAlignVisually_LabelWidget(idMenu, iTrue); | ||
320 | addChild_Widget(navBar, iClob(idMenu)); | ||
321 | //addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f464", 0, 0, "cert.client"))); | ||
317 | #if !defined (iHaveNativeMenus) | 322 | #if !defined (iHaveNativeMenus) |
318 | iLabelWidget *navMenu = | 323 | iLabelWidget *navMenu = |
319 | makeMenuButton_LabelWidget("\U0001d362", navMenuItems, iElemCount(navMenuItems)); | 324 | makeMenuButton_LabelWidget("\U0001d362", navMenuItems, iElemCount(navMenuItems)); |