diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-01 13:55:11 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-01 13:55:11 +0200 |
commit | f4942e1b4da6dc1334dcdb4f2daae670bfa1f813 (patch) | |
tree | 3b0b638d4351311e8322e9c35da11078c27585c0 | |
parent | b3550138da3a669999c06da41720f2be664d5e86 (diff) |
Added switching to the identity toolbar menu
One can now use the identity toolbar menu to switch between client certificates that have been used on the current site. The five latest ones are remembered.
-rw-r--r-- | po/en.po | 9 | ||||
-rw-r--r-- | res/about/version.gmi | 5 | ||||
-rw-r--r-- | res/lang/cs.bin | bin | 30899 -> 30962 bytes | |||
-rw-r--r-- | res/lang/de.bin | bin | 29906 -> 29969 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 26003 -> 26066 bytes | |||
-rw-r--r-- | res/lang/eo.bin | bin | 24957 -> 25020 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 29730 -> 29793 bytes | |||
-rw-r--r-- | res/lang/es_MX.bin | bin | 27062 -> 27125 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 29563 -> 29626 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 30710 -> 30773 bytes | |||
-rw-r--r-- | res/lang/gl.bin | bin | 28915 -> 28978 bytes | |||
-rw-r--r-- | res/lang/hu.bin | bin | 30735 -> 30798 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 28062 -> 28125 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 28650 -> 28713 bytes | |||
-rw-r--r-- | res/lang/isv.bin | bin | 24723 -> 24786 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 29338 -> 29401 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 44098 -> 44161 bytes | |||
-rw-r--r-- | res/lang/sk.bin | bin | 25059 -> 25122 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 43524 -> 43587 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 26772 -> 26835 bytes | |||
-rw-r--r-- | res/lang/tr.bin | bin | 28956 -> 29019 bytes | |||
-rw-r--r-- | res/lang/uk.bin | bin | 43443 -> 43506 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 24957 -> 25020 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 25155 -> 25218 bytes | |||
-rw-r--r-- | src/app.c | 19 | ||||
-rw-r--r-- | src/gmcerts.c | 9 | ||||
-rw-r--r-- | src/gmcerts.h | 5 | ||||
-rw-r--r-- | src/sitespec.c | 81 | ||||
-rw-r--r-- | src/sitespec.h | 20 | ||||
-rw-r--r-- | src/ui/root.c | 89 |
30 files changed, 214 insertions, 23 deletions
@@ -321,6 +321,10 @@ msgstr "Show History" | |||
321 | msgid "menu.show.identities" | 321 | msgid "menu.show.identities" |
322 | msgstr "Show Identities" | 322 | msgstr "Show Identities" |
323 | 323 | ||
324 | # Active identity toolbar menu. | ||
325 | msgid "menu.hide.identities" | ||
326 | msgstr "Hide Identities" | ||
327 | "" | ||
324 | # Used in the View menu on macOS. Shows sidebar and switches sidebar tab. | 328 | # Used in the View menu on macOS. Shows sidebar and switches sidebar tab. |
325 | msgid "menu.show.outline" | 329 | msgid "menu.show.outline" |
326 | msgstr "Show Page Outline" | 330 | msgstr "Show Page Outline" |
@@ -629,6 +633,11 @@ msgstr "Stop Using Everywhere" | |||
629 | msgid "ident.export" | 633 | msgid "ident.export" |
630 | msgstr "Export" | 634 | msgstr "Export" |
631 | 635 | ||
636 | # The %s represents the name of an identity. | ||
637 | #, c-format | ||
638 | msgid "ident.switch" | ||
639 | msgstr "Switch to %s" | ||
640 | |||
632 | msgid "heading.ident.use" | 641 | msgid "heading.ident.use" |
633 | msgstr "IDENTITY USAGE" | 642 | msgstr "IDENTITY USAGE" |
634 | 643 | ||
diff --git a/res/about/version.gmi b/res/about/version.gmi index a9043b2c..7a14e79b 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -6,6 +6,10 @@ | |||
6 | ``` | 6 | ``` |
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 1.10 | ||
10 | New features: | ||
11 | * Identity toolbar menu can be used to switch between alternate identities that have been used on the site. If you have multiple accounts on a site, this makes it more convenient to switch between them. | ||
12 | |||
9 | ## 1.9.2 | 13 | ## 1.9.2 |
10 | * Windows: Use the correct version number for update checks. | 14 | * Windows: Use the correct version number for update checks. |
11 | * Shorter label for "Mark All as Read" in Feeds sidebar actions. | 15 | * Shorter label for "Mark All as Read" in Feeds sidebar actions. |
@@ -22,7 +26,6 @@ | |||
22 | * Fixed the New Tab button not staying at the right edge of the window, depending on how many tabs are open. | 26 | * Fixed the New Tab button not staying at the right edge of the window, depending on how many tabs are open. |
23 | 27 | ||
24 | ## 1.9 | 28 | ## 1.9 |
25 | |||
26 | New features: | 29 | New features: |
27 | * Added a toolbar button for toggling the left sidebar. | 30 | * Added a toolbar button for toggling the left sidebar. |
28 | * Added an unsplit button in the toolbar when in split view mode. | 31 | * Added an unsplit button in the toolbar when in split view mode. |
diff --git a/res/lang/cs.bin b/res/lang/cs.bin index 647e1f34..d3e06c73 100644 --- a/res/lang/cs.bin +++ b/res/lang/cs.bin | |||
Binary files differ | |||
diff --git a/res/lang/de.bin b/res/lang/de.bin index b2bb35a0..b9d155a7 100644 --- a/res/lang/de.bin +++ b/res/lang/de.bin | |||
Binary files differ | |||
diff --git a/res/lang/en.bin b/res/lang/en.bin index fbf4c73c..5bb8299a 100644 --- a/res/lang/en.bin +++ b/res/lang/en.bin | |||
Binary files differ | |||
diff --git a/res/lang/eo.bin b/res/lang/eo.bin index 7156b7c8..ff1ddb01 100644 --- a/res/lang/eo.bin +++ b/res/lang/eo.bin | |||
Binary files differ | |||
diff --git a/res/lang/es.bin b/res/lang/es.bin index 1acb50d1..ba9b2343 100644 --- a/res/lang/es.bin +++ b/res/lang/es.bin | |||
Binary files differ | |||
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin index 078de89d..f4ddcc72 100644 --- a/res/lang/es_MX.bin +++ b/res/lang/es_MX.bin | |||
Binary files differ | |||
diff --git a/res/lang/fi.bin b/res/lang/fi.bin index ccd3e133..f73891d6 100644 --- a/res/lang/fi.bin +++ b/res/lang/fi.bin | |||
Binary files differ | |||
diff --git a/res/lang/fr.bin b/res/lang/fr.bin index 96c1148e..0851f535 100644 --- a/res/lang/fr.bin +++ b/res/lang/fr.bin | |||
Binary files differ | |||
diff --git a/res/lang/gl.bin b/res/lang/gl.bin index 4269b5ed..80215589 100644 --- a/res/lang/gl.bin +++ b/res/lang/gl.bin | |||
Binary files differ | |||
diff --git a/res/lang/hu.bin b/res/lang/hu.bin index 7b7edb50..7d7ed94e 100644 --- a/res/lang/hu.bin +++ b/res/lang/hu.bin | |||
Binary files differ | |||
diff --git a/res/lang/ia.bin b/res/lang/ia.bin index 4750b545..572015c2 100644 --- a/res/lang/ia.bin +++ b/res/lang/ia.bin | |||
Binary files differ | |||
diff --git a/res/lang/ie.bin b/res/lang/ie.bin index e4c90ef9..bb485f4e 100644 --- a/res/lang/ie.bin +++ b/res/lang/ie.bin | |||
Binary files differ | |||
diff --git a/res/lang/isv.bin b/res/lang/isv.bin index 80754fc5..f90a1e7d 100644 --- a/res/lang/isv.bin +++ b/res/lang/isv.bin | |||
Binary files differ | |||
diff --git a/res/lang/pl.bin b/res/lang/pl.bin index c0affedf..753e595b 100644 --- a/res/lang/pl.bin +++ b/res/lang/pl.bin | |||
Binary files differ | |||
diff --git a/res/lang/ru.bin b/res/lang/ru.bin index 966deaea..edcebb14 100644 --- a/res/lang/ru.bin +++ b/res/lang/ru.bin | |||
Binary files differ | |||
diff --git a/res/lang/sk.bin b/res/lang/sk.bin index deda3b69..b843a383 100644 --- a/res/lang/sk.bin +++ b/res/lang/sk.bin | |||
Binary files differ | |||
diff --git a/res/lang/sr.bin b/res/lang/sr.bin index 8d8591e6..1ef302d9 100644 --- a/res/lang/sr.bin +++ b/res/lang/sr.bin | |||
Binary files differ | |||
diff --git a/res/lang/tok.bin b/res/lang/tok.bin index 1b0c0733..6e3c7af7 100644 --- a/res/lang/tok.bin +++ b/res/lang/tok.bin | |||
Binary files differ | |||
diff --git a/res/lang/tr.bin b/res/lang/tr.bin index 60ef518d..71b9382f 100644 --- a/res/lang/tr.bin +++ b/res/lang/tr.bin | |||
Binary files differ | |||
diff --git a/res/lang/uk.bin b/res/lang/uk.bin index 2ed16909..f7040f2f 100644 --- a/res/lang/uk.bin +++ b/res/lang/uk.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin index 3a83dd40..3ccab576 100644 --- a/res/lang/zh_Hans.bin +++ b/res/lang/zh_Hans.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin index 7da4b273..39393417 100644 --- a/res/lang/zh_Hant.bin +++ b/res/lang/zh_Hant.bin | |||
Binary files differ | |||
@@ -3149,6 +3149,25 @@ iBool handleCommand_App(const char *cmd) { | |||
3149 | postCommand_App("idents.changed"); | 3149 | postCommand_App("idents.changed"); |
3150 | return iTrue; | 3150 | return iTrue; |
3151 | } | 3151 | } |
3152 | else if (equal_Command(cmd, "ident.switch")) { | ||
3153 | /* This is different than "ident.signin" in that the currently used identity's activation | ||
3154 | URL is used instead of the current one. */ | ||
3155 | const iString *docUrl = url_DocumentWidget(document_App()); | ||
3156 | const iGmIdentity *cur = identityForUrl_GmCerts(d->certs, docUrl); | ||
3157 | iGmIdentity *dst = findIdentity_GmCerts( | ||
3158 | d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "fp")))); | ||
3159 | if (cur && dst && cur != dst) { | ||
3160 | iString *useUrl = copy_String(findUse_GmIdentity(cur, docUrl)); | ||
3161 | if (isEmpty_String(useUrl)) { | ||
3162 | useUrl = copy_String(docUrl); | ||
3163 | } | ||
3164 | signIn_GmCerts(d->certs, dst, useUrl); | ||
3165 | postCommand_App("idents.changed"); | ||
3166 | postCommand_App("navigate.reload"); | ||
3167 | delete_String(useUrl); | ||
3168 | } | ||
3169 | return iTrue; | ||
3170 | } | ||
3152 | else if (equal_Command(cmd, "idents.changed")) { | 3171 | else if (equal_Command(cmd, "idents.changed")) { |
3153 | saveIdentities_GmCerts(d->certs); | 3172 | saveIdentities_GmCerts(d->certs); |
3154 | return iFalse; | 3173 | return iFalse; |
diff --git a/src/gmcerts.c b/src/gmcerts.c index f95fea7d..345c36e0 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c | |||
@@ -201,6 +201,15 @@ void clearUse_GmIdentity(iGmIdentity *d) { | |||
201 | clear_StringSet(d->useUrls); | 201 | clear_StringSet(d->useUrls); |
202 | } | 202 | } |
203 | 203 | ||
204 | const iString *findUse_GmIdentity(const iGmIdentity *d, const iString *url) { | ||
205 | iConstForEach(StringSet, using, d->useUrls) { | ||
206 | if (startsWith_String(url, cstr_String(using.value))) { | ||
207 | return using.value; | ||
208 | } | ||
209 | } | ||
210 | return NULL; | ||
211 | } | ||
212 | |||
204 | const iString *name_GmIdentity(const iGmIdentity *d) { | 213 | const iString *name_GmIdentity(const iGmIdentity *d) { |
205 | iString *name = collect_String(subject_TlsCertificate(d->cert)); | 214 | iString *name = collect_String(subject_TlsCertificate(d->cert)); |
206 | if (startsWith_String(name, "CN = ")) { | 215 | if (startsWith_String(name, "CN = ")) { |
diff --git a/src/gmcerts.h b/src/gmcerts.h index 02a41c14..6ece1954 100644 --- a/src/gmcerts.h +++ b/src/gmcerts.h | |||
@@ -48,8 +48,9 @@ iBool isUsed_GmIdentity (const iGmIdentity *); | |||
48 | iBool isUsedOn_GmIdentity (const iGmIdentity *, const iString *url); | 48 | iBool isUsedOn_GmIdentity (const iGmIdentity *, const iString *url); |
49 | iBool isUsedOnDomain_GmIdentity (const iGmIdentity *, const iRangecc domain); | 49 | iBool isUsedOnDomain_GmIdentity (const iGmIdentity *, const iRangecc domain); |
50 | 50 | ||
51 | void setUse_GmIdentity (iGmIdentity *, const iString *url, iBool use); | 51 | void setUse_GmIdentity (iGmIdentity *, const iString *url, iBool use); |
52 | void clearUse_GmIdentity (iGmIdentity *); | 52 | void clearUse_GmIdentity (iGmIdentity *); |
53 | const iString *findUse_GmIdentity (const iGmIdentity *, const iString *url); | ||
53 | 54 | ||
54 | const iString *name_GmIdentity(const iGmIdentity *); | 55 | const iString *name_GmIdentity(const iGmIdentity *); |
55 | 56 | ||
diff --git a/src/sitespec.c b/src/sitespec.c index 6f4546f0..f8b77c86 100644 --- a/src/sitespec.c +++ b/src/sitespec.c | |||
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include <the_Foundation/file.h> | 25 | #include <the_Foundation/file.h> |
26 | #include <the_Foundation/path.h> | 26 | #include <the_Foundation/path.h> |
27 | #include <the_Foundation/stringhash.h> | 27 | #include <the_Foundation/stringhash.h> |
28 | #include <the_Foundation/stringarray.h> | ||
28 | #include <the_Foundation/toml.h> | 29 | #include <the_Foundation/toml.h> |
29 | 30 | ||
30 | iDeclareClass(SiteParams) | 31 | iDeclareClass(SiteParams) |
@@ -35,6 +36,7 @@ struct Impl_SiteParams { | |||
35 | uint16_t titanPort; | 36 | uint16_t titanPort; |
36 | iString titanIdentity; /* fingerprint */ | 37 | iString titanIdentity; /* fingerprint */ |
37 | int dismissWarnings; | 38 | int dismissWarnings; |
39 | iStringArray usedIdentities; /* fingerprints; latest ones at the end */ | ||
38 | /* TODO: theme seed, style settings */ | 40 | /* TODO: theme seed, style settings */ |
39 | }; | 41 | }; |
40 | 42 | ||
@@ -42,12 +44,23 @@ void init_SiteParams(iSiteParams *d) { | |||
42 | d->titanPort = 0; /* undefined */ | 44 | d->titanPort = 0; /* undefined */ |
43 | init_String(&d->titanIdentity); | 45 | init_String(&d->titanIdentity); |
44 | d->dismissWarnings = 0; | 46 | d->dismissWarnings = 0; |
47 | init_StringArray(&d->usedIdentities); | ||
45 | } | 48 | } |
46 | 49 | ||
47 | void deinit_SiteParams(iSiteParams *d) { | 50 | void deinit_SiteParams(iSiteParams *d) { |
51 | deinit_StringArray(&d->usedIdentities); | ||
48 | deinit_String(&d->titanIdentity); | 52 | deinit_String(&d->titanIdentity); |
49 | } | 53 | } |
50 | 54 | ||
55 | static size_t findUsedIdentity_SiteParams_(const iSiteParams *d, const iString *fingerprint) { | ||
56 | iConstForEach(StringArray, i, &d->usedIdentities) { | ||
57 | if (equal_String(i.value, fingerprint)) { | ||
58 | return index_StringArrayConstIterator(&i); | ||
59 | } | ||
60 | } | ||
61 | return iInvalidPos; | ||
62 | } | ||
63 | |||
51 | iDefineClass(SiteParams) | 64 | iDefineClass(SiteParams) |
52 | iDefineObjectConstruction(SiteParams) | 65 | iDefineObjectConstruction(SiteParams) |
53 | 66 | ||
@@ -130,6 +143,12 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con | |||
130 | else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) { | 143 | else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) { |
131 | d->loadParams->dismissWarnings = value->value.int64; | 144 | d->loadParams->dismissWarnings = value->value.int64; |
132 | } | 145 | } |
146 | else if (!cmp_String(key, "usedIdentities") && value->type == string_TomlType) { | ||
147 | iRangecc seg = iNullRange; | ||
148 | while (nextSplit_Rangecc(range_String(value->value.string), " ", &seg)) { | ||
149 | pushBack_StringArray(&d->loadParams->usedIdentities, collectNewRange_String(seg)); | ||
150 | } | ||
151 | } | ||
133 | } | 152 | } |
134 | 153 | ||
135 | static iBool load_SiteSpec_(iSiteSpec *d) { | 154 | static iBool load_SiteSpec_(iSiteSpec *d) { |
@@ -151,6 +170,7 @@ static void save_SiteSpec_(iSiteSpec *d) { | |||
151 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | 170 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { |
152 | iString *buf = new_String(); | 171 | iString *buf = new_String(); |
153 | iConstForEach(StringHash, i, &d->sites) { | 172 | iConstForEach(StringHash, i, &d->sites) { |
173 | iBeginCollect(); | ||
154 | const iBlock * key = &i.value->keyBlock; | 174 | const iBlock * key = &i.value->keyBlock; |
155 | const iSiteParams *params = i.value->object; | 175 | const iSiteParams *params = i.value->object; |
156 | format_String(buf, "[%s]\n", cstr_Block(key)); | 176 | format_String(buf, "[%s]\n", cstr_Block(key)); |
@@ -164,8 +184,15 @@ static void save_SiteSpec_(iSiteSpec *d) { | |||
164 | if (params->dismissWarnings) { | 184 | if (params->dismissWarnings) { |
165 | appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings); | 185 | appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings); |
166 | } | 186 | } |
187 | if (!isEmpty_StringArray(¶ms->usedIdentities)) { | ||
188 | appendFormat_String( | ||
189 | buf, | ||
190 | "usedIdentities = \"%s\"\n", | ||
191 | cstrCollect_String(joinCStr_StringArray(¶ms->usedIdentities, " "))); | ||
192 | } | ||
167 | appendCStr_String(buf, "\n"); | 193 | appendCStr_String(buf, "\n"); |
168 | write_File(f, utf8_String(buf)); | 194 | write_File(f, utf8_String(buf)); |
195 | iEndCollect(); | ||
169 | } | 196 | } |
170 | delete_String(buf); | 197 | delete_String(buf); |
171 | } | 198 | } |
@@ -188,14 +215,19 @@ void deinit_SiteSpec(void) { | |||
188 | deinit_String(&d->saveDir); | 215 | deinit_String(&d->saveDir); |
189 | } | 216 | } |
190 | 217 | ||
191 | void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) { | 218 | static iSiteParams *findParams_SiteSpec_(iSiteSpec *d, const iString *site) { |
192 | iSiteSpec *d = &siteSpec_; | ||
193 | const iString *hashKey = collect_String(lower_String(site)); | 219 | const iString *hashKey = collect_String(lower_String(site)); |
194 | iSiteParams *params = value_StringHash(&d->sites, hashKey); | 220 | iSiteParams *params = value_StringHash(&d->sites, hashKey); |
195 | if (!params) { | 221 | if (!params) { |
196 | params = new_SiteParams(); | 222 | params = new_SiteParams(); |
197 | insert_StringHash(&d->sites, hashKey, params); | 223 | insert_StringHash(&d->sites, hashKey, params); |
198 | } | 224 | } |
225 | return params; | ||
226 | } | ||
227 | |||
228 | void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) { | ||
229 | iSiteSpec *d = &siteSpec_; | ||
230 | iSiteParams *params = findParams_SiteSpec_(d, site); | ||
199 | iBool needSave = iFalse; | 231 | iBool needSave = iFalse; |
200 | switch (key) { | 232 | switch (key) { |
201 | case titanPort_SiteSpecKey: | 233 | case titanPort_SiteSpecKey: |
@@ -216,12 +248,7 @@ void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) { | |||
216 | 248 | ||
217 | void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { | 249 | void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { |
218 | iSiteSpec *d = &siteSpec_; | 250 | iSiteSpec *d = &siteSpec_; |
219 | const iString *hashKey = collect_String(lower_String(site)); | 251 | iSiteParams *params = findParams_SiteSpec_(d, site); |
220 | iSiteParams *params = value_StringHash(&d->sites, hashKey); | ||
221 | if (!params) { | ||
222 | params = new_SiteParams(); | ||
223 | insert_StringHash(&d->sites, hashKey, params); | ||
224 | } | ||
225 | iBool needSave = iFalse; | 252 | iBool needSave = iFalse; |
226 | switch (key) { | 253 | switch (key) { |
227 | case titanIdentity_SiteSpecKey: | 254 | case titanIdentity_SiteSpecKey: |
@@ -238,6 +265,44 @@ void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const i | |||
238 | } | 265 | } |
239 | } | 266 | } |
240 | 267 | ||
268 | static void insertOrRemoveString_SiteSpec_(iSiteSpec *d, const iString *site, enum iSiteSpecKey key, | ||
269 | const iString *value, iBool doInsert) { | ||
270 | iSiteParams *params = findParams_SiteSpec_(d, site); | ||
271 | iBool needSave = iFalse; | ||
272 | switch (key) { | ||
273 | case usedIdentities_SiteSpecKey: { | ||
274 | const size_t index = findUsedIdentity_SiteParams_(params, value); | ||
275 | if (doInsert && index == iInvalidPos) { | ||
276 | pushBack_StringArray(¶ms->usedIdentities, value); | ||
277 | needSave = iTrue; | ||
278 | } | ||
279 | else if (!doInsert && index != iInvalidPos) { | ||
280 | remove_StringArray(¶ms->usedIdentities, index); | ||
281 | needSave = iTrue; | ||
282 | } | ||
283 | break; | ||
284 | } | ||
285 | default: | ||
286 | break; | ||
287 | } | ||
288 | if (needSave) { | ||
289 | save_SiteSpec_(d); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | void insertString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { | ||
294 | insertOrRemoveString_SiteSpec_(&siteSpec_, site, key, value, iTrue); | ||
295 | } | ||
296 | |||
297 | void removeString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { | ||
298 | insertOrRemoveString_SiteSpec_(&siteSpec_, site, key, value, iFalse); | ||
299 | } | ||
300 | |||
301 | const iStringArray *strings_SiteSpec(const iString *site, enum iSiteSpecKey key) { | ||
302 | const iSiteParams *params = findParams_SiteSpec_(&siteSpec_, site); | ||
303 | return ¶ms->usedIdentities; | ||
304 | } | ||
305 | |||
241 | int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { | 306 | int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { |
242 | iSiteSpec *d = &siteSpec_; | 307 | iSiteSpec *d = &siteSpec_; |
243 | const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); | 308 | const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); |
diff --git a/src/sitespec.h b/src/sitespec.h index 5adaeb8c..11c40e3c 100644 --- a/src/sitespec.h +++ b/src/sitespec.h | |||
@@ -22,22 +22,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include <the_Foundation/string.h> | 25 | #include <the_Foundation/stringarray.h> |
26 | 26 | ||
27 | iDeclareType(SiteSpec) | 27 | iDeclareType(SiteSpec) |
28 | 28 | ||
29 | enum iSiteSpecKey { | 29 | enum iSiteSpecKey { |
30 | titanPort_SiteSpecKey, | 30 | titanPort_SiteSpecKey, /* int */ |
31 | titanIdentity_SiteSpecKey, | 31 | titanIdentity_SiteSpecKey, /* String */ |
32 | dismissWarnings_SiteSpecKey, | 32 | dismissWarnings_SiteSpecKey, /* int */ |
33 | usedIdentities_SiteSpecKey, /* StringArray */ | ||
33 | }; | 34 | }; |
34 | 35 | ||
35 | void init_SiteSpec (const char *saveDir); | 36 | void init_SiteSpec (const char *saveDir); |
36 | void deinit_SiteSpec (void); | 37 | void deinit_SiteSpec (void); |
37 | 38 | ||
38 | /* changes saved immediately */ | 39 | /* changes saved immediately */ |
39 | void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); | 40 | void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); |
40 | void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); | 41 | void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); |
42 | void insertString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); | ||
43 | void removeString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); | ||
41 | 44 | ||
42 | int value_SiteSpec (const iString *site, enum iSiteSpecKey key); | 45 | int value_SiteSpec (const iString *site, enum iSiteSpecKey key); |
43 | const iString * valueString_SiteSpec (const iString *site, enum iSiteSpecKey key); | 46 | const iString * valueString_SiteSpec (const iString *site, enum iSiteSpecKey key); |
47 | const iStringArray *strings_SiteSpec (const iString *site, enum iSiteSpecKey key); | ||
diff --git a/src/ui/root.c b/src/ui/root.c index c2161d80..f06ae842 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
38 | #include "../history.h" | 38 | #include "../history.h" |
39 | #include "../gmcerts.h" | 39 | #include "../gmcerts.h" |
40 | #include "../gmutil.h" | 40 | #include "../gmutil.h" |
41 | #include "../sitespec.h" | ||
41 | #include "../visited.h" | 42 | #include "../visited.h" |
42 | 43 | ||
43 | #if defined (iPlatformMsys) | 44 | #if defined (iPlatformMsys) |
@@ -330,6 +331,66 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
330 | openMenuFlags_Widget(menu, zero_I2(), postCommands_MenuOpenFlags | center_MenuOpenFlags); | 331 | openMenuFlags_Widget(menu, zero_I2(), postCommands_MenuOpenFlags | center_MenuOpenFlags); |
331 | return iTrue; | 332 | return iTrue; |
332 | } | 333 | } |
334 | else if (equal_Command(cmd, "identmenu.open")) { | ||
335 | iWidget *button = findWidget_Root("navbar.ident"); | ||
336 | iArray items; | ||
337 | init_Array(&items, sizeof(iMenuItem)); | ||
338 | /* Current identity. */ | ||
339 | const iString *docUrl = url_DocumentWidget(document_App()); | ||
340 | const iGmIdentity *ident = identityForUrl_GmCerts(certs_App(), docUrl); | ||
341 | const iString *fp = collect_String(hexEncode_Block(&ident->fingerprint)); | ||
342 | pushBackN_Array(&items, | ||
343 | (iMenuItem[]){ { format_CStr("///" uiHeading_ColorEscape "%s", | ||
344 | ident ? cstr_String(name_GmIdentity(ident)) | ||
345 | : "${menu.identity.notactive}") }, | ||
346 | { "---" } }, | ||
347 | 2); | ||
348 | /* Alternate identities. */ { | ||
349 | const iString *site = collectNewRange_String(urlRoot_String(docUrl)); | ||
350 | iBool haveAlts = iFalse; | ||
351 | iConstForEach(StringArray, i, strings_SiteSpec(site, usedIdentities_SiteSpecKey)) { | ||
352 | if (!equal_String(i.value, fp)) { | ||
353 | const iBlock *otherFp = collect_Block(hexDecode_Rangecc(range_String(i.value))); | ||
354 | const iGmIdentity *other = findIdentity_GmCerts(certs_App(), otherFp); | ||
355 | if (other) { | ||
356 | pushBack_Array( | ||
357 | &items, | ||
358 | &(iMenuItem){ | ||
359 | format_CStr(translateCStr_Lang("\U0001f816 ${ident.switch}"), | ||
360 | cstr_String(name_GmIdentity(other))), | ||
361 | 0, | ||
362 | 0, | ||
363 | format_CStr("ident.switch fp:%s", cstr_String(i.value)) }); | ||
364 | haveAlts = iTrue; | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | if (haveAlts) { | ||
369 | pushBack_Array(&items, &(iMenuItem){ "---" }); | ||
370 | } | ||
371 | } | ||
372 | iSidebarWidget *sidebar = findWidget_App("sidebar"); | ||
373 | pushBackN_Array( | ||
374 | &items, | ||
375 | (iMenuItem[]){ | ||
376 | { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" }, | ||
377 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, | ||
378 | { "---" }, | ||
379 | { isVisible_Widget(sidebar) && mode_SidebarWidget(sidebar) == identities_SidebarMode | ||
380 | ? leftHalf_Icon " ${menu.hide.identities}" | ||
381 | : leftHalf_Icon " ${menu.show.identities}", | ||
382 | 0, | ||
383 | 0, | ||
384 | deviceType_App() == phone_AppDeviceType ? "toolbar.showident" | ||
385 | : "sidebar.mode arg:3 toggle:1" }, | ||
386 | }, | ||
387 | 4); | ||
388 | iWidget *menu = | ||
389 | makeMenu_Widget(button, constData_Array(&items), size_Array(&items)); | ||
390 | openMenu_Widget(menu, topLeft_Rect(bounds_Widget(button))); | ||
391 | deinit_Array(&items); | ||
392 | return iTrue; | ||
393 | } | ||
333 | else if (equal_Command(cmd, "contextclick")) { | 394 | else if (equal_Command(cmd, "contextclick")) { |
334 | iBool showBarMenu = iFalse; | 395 | iBool showBarMenu = iFalse; |
335 | if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) { | 396 | if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) { |
@@ -780,6 +841,26 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
780 | dismissPortraitPhoneSidebars_Root(get_Root()); | 841 | dismissPortraitPhoneSidebars_Root(get_Root()); |
781 | updateNavBarIdentity_(navBar); | 842 | updateNavBarIdentity_(navBar); |
782 | updateNavDirButtons_(navBar); | 843 | updateNavDirButtons_(navBar); |
844 | /* Update site-specific used identities. */ { | ||
845 | const iGmIdentity *ident = | ||
846 | identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App())); | ||
847 | if (ident) { | ||
848 | const iString *site = | ||
849 | collectNewRange_String(urlRoot_String(canonicalUrl_String(urlStr))); | ||
850 | const iStringArray *usedIdents = | ||
851 | strings_SiteSpec(site, usedIdentities_SiteSpecKey); | ||
852 | const iString *fingerprint = collect_String(hexEncode_Block(&ident->fingerprint)); | ||
853 | /* Keep this identity at the end of the list. */ | ||
854 | removeString_SiteSpec(site, usedIdentities_SiteSpecKey, fingerprint); | ||
855 | insertString_SiteSpec(site, usedIdentities_SiteSpecKey, fingerprint); | ||
856 | /* Keep the list short. */ | ||
857 | while (size_StringArray(usedIdents) > 5) { | ||
858 | removeString_SiteSpec(site, | ||
859 | usedIdentities_SiteSpecKey, | ||
860 | constAt_StringArray(usedIdents, 0)); | ||
861 | } | ||
862 | } | ||
863 | } | ||
783 | /* Icon updates should be limited to automatically chosen icons if the user | 864 | /* Icon updates should be limited to automatically chosen icons if the user |
784 | is allowed to pick their own in the future. */ | 865 | is allowed to pick their own in the future. */ |
785 | if (updateBookmarkIcon_Bookmarks(bookmarks_App(), urlStr, | 866 | if (updateBookmarkIcon_Bookmarks(bookmarks_App(), urlStr, |
@@ -1268,10 +1349,10 @@ void createUserInterface_Root(iRoot *d) { | |||
1268 | setId_Widget(addChild_Widget(rightEmbed, iClob(makePadding_Widget(0))), "url.embedpad"); | 1349 | setId_Widget(addChild_Widget(rightEmbed, iClob(makePadding_Widget(0))), "url.embedpad"); |
1269 | } | 1350 | } |
1270 | /* The active identity menu. */ { | 1351 | /* The active identity menu. */ { |
1271 | iLabelWidget *idMenu = makeMenuButton_LabelWidget( | 1352 | iLabelWidget *idButton = new_LabelWidget(person_Icon, "identmenu.open"); |
1272 | "\U0001f464", identityButtonMenuItems_, iElemCount(identityButtonMenuItems_)); | 1353 | // "\U0001f464", identityButtonMenuItems_, iElemCount(identityButtonMenuItems_)); |
1273 | setAlignVisually_LabelWidget(idMenu, iTrue); | 1354 | setAlignVisually_LabelWidget(idButton, iTrue); |
1274 | setId_Widget(addChildFlags_Widget(navBar, iClob(idMenu), collapse_WidgetFlag), "navbar.ident"); | 1355 | setId_Widget(addChildFlags_Widget(navBar, iClob(idButton), collapse_WidgetFlag), "navbar.ident"); |
1275 | } | 1356 | } |
1276 | addChildFlags_Widget(navBar, iClob(new_Widget()), expand_WidgetFlag); | 1357 | addChildFlags_Widget(navBar, iClob(new_Widget()), expand_WidgetFlag); |
1277 | setId_Widget(addChildFlags_Widget(navBar, | 1358 | setId_Widget(addChildFlags_Widget(navBar, |