Lagrange [work/v1.7]

Site-specific Titan upload identity

=> c58985ed3105ff7fc29fc0d53ba81ba55882e05e

diff --git a/res/about/version.gmi b/res/about/version.gmi
index 020373e4..b5750c8c 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -8,6 +8,8 @@
 
 ## 1.8.3
 * Fixed clicking on UI elements that are over the page top banner. The banner would always get clicked instead.
+* Titan upload identity is remembered as a site-specific setting. It is no longer affected by selections in the Identities sidebar.
+* macOS: Fixed updating items in native menus, e.g., upload identity selection.
 
 ## 1.8.2
 * Fixed encoding of `+` characters in URLs as per RFC 3986.
diff --git a/src/gmcerts.c b/src/gmcerts.c
index 36fd7d55..f95fea7d 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -295,6 +295,9 @@ static void loadIdentities_GmCerts_(iGmCerts *d) {
 }
 
 iGmIdentity *findIdentity_GmCerts(iGmCerts *d, const iBlock *fingerprint) {
+    if (isEmpty_Block(fingerprint)) {
+        return NULL;
+    }
     iForEach(PtrArray, i, &d->idents) {
         iGmIdentity *ident = i.ptr;
         if (cmp_Block(fingerprint, &ident->fingerprint) == 0) { /* TODO: could use a hash */
@@ -549,6 +552,9 @@ const iGmIdentity *constIdentity_GmCerts(const iGmCerts *d, unsigned int id) {
 }
 
 const iGmIdentity *identityForUrl_GmCerts(const iGmCerts *d, const iString *url) {
+    if (isEmpty_String(url)) {
+        return NULL;
+    }
     lock_Mutex(d->mtx);
     const iGmIdentity *found = NULL;
     iConstForEach(PtrArray, i, &d->idents) {
diff --git a/src/sitespec.c b/src/sitespec.c
index 0332af2d..6f4546f0 100644
--- a/src/sitespec.c
+++ b/src/sitespec.c
@@ -33,17 +33,19 @@ iDeclareObjectConstruction(SiteParams)
 struct Impl_SiteParams {
     iObject  object;
     uint16_t titanPort;
+    iString  titanIdentity; /* fingerprint */
     int      dismissWarnings;
     /* TODO: theme seed, style settings */
 };
 
 void init_SiteParams(iSiteParams *d) {
-    d->titanPort       = 0; /* undefined */
+    d->titanPort = 0; /* undefined */
+    init_String(&d->titanIdentity);
     d->dismissWarnings = 0;
 }
 
 void deinit_SiteParams(iSiteParams *d) {
-    iUnused(d);
+    deinit_String(&d->titanIdentity);
 }
 
 iDefineClass(SiteParams)
@@ -122,6 +124,9 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con
     if (!cmp_String(key, "titanPort")) {
         d->loadParams->titanPort = number_TomlValue(value);
     }
+    else if (!cmp_String(key, "titanIdentity") && value->type == string_TomlType) {
+        set_String(&d->loadParams->titanIdentity, value->value.string);
+    }
     else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) {
         d->loadParams->dismissWarnings = value->value.int64;
     }
@@ -152,6 +157,10 @@ static void save_SiteSpec_(iSiteSpec *d) {
             if (params->titanPort) {
                 appendFormat_String(buf, "titanPort = %u\n", params->titanPort);
             }
+            if (!isEmpty_String(¶ms->titanIdentity)) {
+                appendFormat_String(
+                    buf, "titanIdentity = \"%s\"\n", cstr_String(¶ms->titanIdentity));
+            }
             if (params->dismissWarnings) {
                 appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings);
             }
@@ -205,6 +214,30 @@ void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) {
     }
 }
 
+void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) {
+    iSiteSpec *d = &siteSpec_;
+    const iString *hashKey = collect_String(lower_String(site));
+    iSiteParams *params = value_StringHash(&d->sites, hashKey);
+    if (!params) {
+        params = new_SiteParams();
+        insert_StringHash(&d->sites, hashKey, params);
+    }
+    iBool needSave = iFalse;
+    switch (key) {
+        case titanIdentity_SiteSpecKey:
+            if (!equal_String(¶ms->titanIdentity, value)) {
+                needSave = iTrue;
+                set_String(¶ms->titanIdentity, value);
+            }
+            break;
+        default:
+            break;
+    }
+    if (needSave) {
+        save_SiteSpec_(d);
+    }
+}
+
 int value_SiteSpec(const iString *site, enum iSiteSpecKey key) {
     iSiteSpec *d = &siteSpec_;
     const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site)));
@@ -220,3 +253,17 @@ int value_SiteSpec(const iString *site, enum iSiteSpecKey key) {
             return 0;
     }    
 }
+
+const iString *valueString_SiteSpec(const iString *site, enum iSiteSpecKey key) {
+    iSiteSpec *d = &siteSpec_;
+    const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site)));
+    if (!params) {
+        return 0;
+    }
+    switch (key) {
+        case titanIdentity_SiteSpecKey:
+            return ¶ms->titanIdentity;
+        default:
+            return collectNew_String();
+    }    
+}
diff --git a/src/sitespec.h b/src/sitespec.h
index 6b64f073..5adaeb8c 100644
--- a/src/sitespec.h
+++ b/src/sitespec.h
@@ -28,11 +28,16 @@ iDeclareType(SiteSpec)
     
 enum iSiteSpecKey {
     titanPort_SiteSpecKey,
+    titanIdentity_SiteSpecKey,
     dismissWarnings_SiteSpecKey,
 };
     
 void    init_SiteSpec       (const char *saveDir);
 void    deinit_SiteSpec     (void);
 
-void    setValue_SiteSpec   (const iString *site, enum iSiteSpecKey key, int value); /* changes saved immediately */
-int     value_SiteSpec      (const iString *site, enum iSiteSpecKey key);
+/* changes saved immediately */
+void    setValue_SiteSpec       (const iString *site, enum iSiteSpecKey key, int value); 
+void    setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value);
+
+int             value_SiteSpec          (const iString *site, enum iSiteSpecKey key);
+const iString * valueString_SiteSpec    (const iString *site, enum iSiteSpecKey key);
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index 90df1958..bad00071 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -45,7 +45,7 @@ iDefineObjectConstruction(UploadWidget)
     
 enum iUploadIdentity {
     none_UploadIdentity,
-    defaultForUrl_UploadIdentity,
+    defaultForSite_UploadIdentity,
     dropdown_UploadIdentity,
 };
 
@@ -104,9 +104,16 @@ static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) {
                                     (avail - inputPos.y) / lineHeight_Text(font_InputWidget(d->input))));
 }
 
+static const iGmIdentity *titanIdentityForUrl_(const iString *url) {
+    return findIdentity_GmCerts(
+        certs_App(),
+        collect_Block(hexDecode_Rangecc(range_String(valueString_SiteSpec(
+            collectNewRange_String(urlRoot_String(url)), titanIdentity_SiteSpecKey)))));
+}
+
 static const iArray *makeIdentityItems_UploadWidget_(const iUploadWidget *d) {
     iArray *items = collectNew_Array(sizeof(iMenuItem));
-    const iGmIdentity *urlId = identityForUrl_GmCerts(certs_App(), &d->url);
+    const iGmIdentity *urlId = titanIdentityForUrl_(&d->url);
     pushBack_Array(items,
                    &(iMenuItem){ format_CStr("${dlg.upload.id.default} (%s)",
                                              urlId ? cstr_String(name_GmIdentity(urlId))
@@ -147,7 +154,7 @@ void init_UploadWidget(iUploadWidget *d) {
     d->request = NULL;
     init_String(&d->filePath);
     d->fileSize = 0;
-    d->idMode = defaultForUrl_UploadIdentity;
+    d->idMode = defaultForSite_UploadIdentity;
     init_Block(&d->idFingerprint, 0);
     const iMenuItem actions[] = {
         { "${upload.port}", 0, 0, "upload.setport" },
@@ -289,16 +296,22 @@ void deinit_UploadWidget(iUploadWidget *d) {
 
 static void remakeIdentityItems_UploadWidget_(iUploadWidget *d) {
     iWidget *dropMenu = findChild_Widget(findChild_Widget(as_Widget(d), "upload.id"), "menu");
-    releaseChildren_Widget(dropMenu);
     const iArray *items = makeIdentityItems_UploadWidget_(d);
-    makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items));
+    /* TODO: Make the following a utility method. */
+    if (flags_Widget(dropMenu) & nativeMenu_WidgetFlag) {
+        setNativeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items));
+    }
+    else {
+        releaseChildren_Widget(dropMenu);
+        makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items));
+    }
 }
 
 static void updateIdentityDropdown_UploadWidget_(iUploadWidget *d) {
     updateDropdownSelection_LabelWidget(
         findChild_Widget(as_Widget(d), "upload.id"),
         d->idMode == none_UploadIdentity ? " arg:0"
-        : d->idMode == defaultForUrl_UploadIdentity
+        : d->idMode == defaultForSite_UploadIdentity
             ? " arg:1"
             : format_CStr(" fp:%s", cstrCollect_String(hexEncode_Block(&d->idFingerprint))));
 }
@@ -422,7 +435,7 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
         }
         else if (arg_Command(cmd)) {
             clear_Block(&d->idFingerprint);
-            d->idMode = defaultForUrl_UploadIdentity;
+            d->idMode = defaultForSite_UploadIdentity;
         }
         else {
             clear_Block(&d->idFingerprint);
@@ -452,19 +465,27 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
         setSendProgressFunc_GmRequest(d->request, updateProgress_UploadWidget_);
         setUserData_Object(d->request, d);
         setUrl_GmRequest(d->request, &d->url);
+        const iString     *site    = collectNewRange_String(urlRoot_String(&d->url));
         switch (d->idMode) {
-            case defaultForUrl_UploadIdentity:
-                break; /* GmRequest handles it */
             case none_UploadIdentity:
-                setIdentity_GmRequest(d->request, NULL);
+                /* Ensure no identity will be used for this specific URL. */
                 signOut_GmCerts(certs_App(), url_GmRequest(d->request));
+                setValueString_SiteSpec(site, titanIdentity_SiteSpecKey, collectNew_String());
                 break;
             case dropdown_UploadIdentity: {
                 iGmIdentity *ident = findIdentity_GmCerts(certs_App(), &d->idFingerprint);
-                setIdentity_GmRequest(d->request, ident);
-                signIn_GmCerts(certs_App(), ident, url_GmRequest(d->request));
+                if (ident) {
+                    setValueString_SiteSpec(site,
+                                            titanIdentity_SiteSpecKey,
+                                            collect_String(hexEncode_Block(&ident->fingerprint)));
+                }
                 break;
             }
+            default:
+                break;
+        }
+        if (d->idMode != none_UploadIdentity) {
+            setIdentity_GmRequest(d->request, titanIdentityForUrl_(&d->url));
         }
         if (isText) {
             /* Uploading text. */
diff --git a/src/ui/util.c b/src/ui/util.c
index 2624bf2b..0a9dde0c 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -809,14 +809,27 @@ static void deleteMenuItems_(iArray *items) {
     delete_Array(items);
 }
 
-iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
-    iWidget *menu = new_Widget();
+void releaseNativeMenu_Widget(iWidget *d) {
 #if defined (iHaveNativeContextMenus)
-    setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue);
+    iArray *items = userData_Object(d);
+    if (items) {
+        iAssert(flags_Widget(d) & nativeMenu_WidgetFlag);
+        iAssert(items);
+        deleteMenuItems_(items);
+        setUserData_Object(d, NULL);
+    }
+#else
+    iUnused(d);
+#endif
+}
+
+void setNativeMenuItems_Widget(iWidget *menu, const iMenuItem *items, size_t n) {
+#if defined (iHaveNativeContextMenus)
+    iAssert(flags_Widget(menu) & nativeMenu_WidgetFlag);
+    releaseNativeMenu_Widget(menu);
     setUserData_Object(menu, deepCopyMenuItems_(menu, items, n));
-    addChild_Widget(parent, menu);
-    iRelease(menu); /* owned by parent now */
     /* Keyboard shortcuts still need to triggerable via the menu, although the items don't exist. */ {
+        releaseChildren_Widget(menu);
         for (size_t i = 0; i < n; i++) {
             const iMenuItem *item = &items[i];
             if (item->key) {
@@ -824,6 +837,17 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
             }
         }
     }
+#endif    
+}
+
+iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
+    iWidget *menu = new_Widget();
+#if defined (iHaveNativeContextMenus)
+    setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue);
+    addChild_Widget(parent, menu);
+    iRelease(menu); /* owned by parent now */
+    setUserData_Object(menu, NULL);
+    setNativeMenuItems_Widget(menu, items, n);
 #else
     /* Non-native custom popup menu. This may still be displayed inside a separate window. */
     setDrawBufferEnabled_Widget(menu, iTrue);
@@ -990,18 +1014,6 @@ iLocalDef iBool isUsingMenuPopupWindows_(void) {
 #endif
 }
 
-void releaseNativeMenu_Widget(iWidget *d) {
-#if defined (iHaveNativeContextMenus)
-    iArray *items = userData_Object(d);
-    iAssert(flags_Widget(d) & nativeMenu_WidgetFlag);
-    iAssert(items);
-    deleteMenuItems_(items);
-    setUserData_Object(d, NULL);
-#else
-    iUnused(d);
-#endif
-}
-
 void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) {
     const iBool postCommands = (menuOpenFlags & postCommands_MenuOpenFlags) != 0;
 #if defined (iHaveNativeContextMenus)
@@ -1263,8 +1275,8 @@ void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *s
         iMenuItem *item = findNativeMenuItem_Widget(menu, selectedCommand);
         if (item) {
             setSelected_NativeMenuItem(item, iTrue);
-            updateText_LabelWidget(dropButton,
-                                   removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label)));
+            updateText_LabelWidget(
+                dropButton, removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label)));
         }
         return;
     }
diff --git a/src/ui/util.h b/src/ui/util.h
index cf96dfe4..52b3a692 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -249,6 +249,7 @@ void            setMenuItemDisabled_Widget      (iWidget *menu, const char *comm
 void            setMenuItemDisabledByIndex_Widget(iWidget *menu, size_t index, iBool disable);
 void            setMenuItemLabel_Widget         (iWidget *menu, const char *command, const char *newLabel);
 void            setMenuItemLabelByIndex_Widget  (iWidget *menu, size_t index, const char *newLabel);
+void            setNativeMenuItems_Widget       (iWidget *, const iMenuItem *items, size_t n);
 
 int             checkContextMenu_Widget         (iWidget *, const SDL_Event *ev); /* see macro below */
 
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/cdiff/c58985ed3105ff7fc29fc0d53ba81ba55882e05e
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
69.962731 milliseconds
Gemini-to-HTML Time
0.591842 milliseconds

This content has been proxied by September (ba2dc).