[PATCH 5/5] winemenubuilder: Create .desktop files for programs that open URIs

Alex Henrie alexhenrie24 at gmail.com
Mon Oct 4 00:51:26 CDT 2021


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22904
Signed-off-by: Alex Henrie <alexhenrie24 at gmail.com>
---
 loader/wine.inf.in                         |   4 +
 programs/winemenubuilder/winemenubuilder.c | 172 ++++++++++++---------
 2 files changed, 102 insertions(+), 74 deletions(-)

diff --git a/loader/wine.inf.in b/loader/wine.inf.in
index b2747ad9a34..ef1a0e759d7 100644
--- a/loader/wine.inf.in
+++ b/loader/wine.inf.in
@@ -516,6 +516,10 @@ HKCU,Software\Wine\FileOpenNoIntegration\.txt,"notepad",,"""%11%\notepad.exe"" "
 HKCU,Software\Wine\FileOpenNoIntegration\.url,"ieframe",,"rundll32.exe ieframe.dll,OpenURL %l"
 HKCU,Software\Wine\FileOpenNoIntegration\.wri,"wordpad",,"""%16422%\Windows NT\Accessories\wordpad.exe"" ""%1"""
 HKCU,Software\Wine\FileOpenNoIntegration\.xml,"winebrowser",,"""%11%\winebrowser.exe"" ""%1"""
+HKCU,Software\Wine\FileOpenNoIntegration\ftp,"winebrowser",,"""%11%\winebrowser.exe"" ""%1"""
+HKCU,Software\Wine\FileOpenNoIntegration\http,"winebrowser",,"""%11%\winebrowser.exe"" ""%1"""
+HKCU,Software\Wine\FileOpenNoIntegration\https,"winebrowser",,"""%11%\winebrowser.exe"" ""%1"""
+HKCU,Software\Wine\FileOpenNoIntegration\mailto,"winebrowser",,"""%11%\winebrowser.exe"" ""%1"""
 
 [ContentIndex]
 HKLM,System\CurrentControlSet\Control\ContentIndex\Language\Neutral,"WBreakerClass",,"{369647e0-17b0-11ce-9950-00aa004bbb1f}"
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index a2790cf203d..88d41cd9e7b 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -1933,10 +1933,13 @@ static BOOL has_association_changed(LPCWSTR extensionW, const WCHAR *mimeType, c
             ret = TRUE;
         heap_free(value);
 
-        value = reg_get_valW(assocKey, extensionW, L"ProgID");
-        if (!value || wcscmp(value, progId))
-            ret = TRUE;
-        heap_free(value);
+        if (progId)
+        {
+            value = reg_get_valW(assocKey, extensionW, L"ProgID");
+            if (!value || wcscmp(value, progId))
+                ret = TRUE;
+            heap_free(value);
+        }
 
         value = reg_get_valW(assocKey, extensionW, L"AppName");
         if (!value || wcscmp(value, appName))
@@ -1980,7 +1983,7 @@ static void update_association(LPCWSTR extension, const WCHAR *mimeType, const W
     }
 
     RegSetValueExW(subkey, L"MimeType", 0, REG_SZ, (const BYTE*) mimeType, (lstrlenW(mimeType) + 1) * sizeof(WCHAR));
-    RegSetValueExW(subkey, L"ProgID", 0, REG_SZ, (const BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR));
+    if (progId) RegSetValueExW(subkey, L"ProgID", 0, REG_SZ, (const BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR));
     RegSetValueExW(subkey, L"AppName", 0, REG_SZ, (const BYTE*) appName, (lstrlenW(appName) + 1) * sizeof(WCHAR));
     RegSetValueExW(subkey, L"DesktopFile", 0, REG_SZ, (const BYTE*) desktopFile, (lstrlenW(desktopFile) + 1) * sizeof(WCHAR));
     if (openWithIcon)
@@ -2091,7 +2094,7 @@ static BOOL is_extension_banned(LPCWSTR extension)
     return FALSE;
 }
 
-static BOOL on_exclude_list(const WCHAR *extension, const WCHAR *command)
+static BOOL on_exclude_list(const WCHAR *win_type, const WCHAR *command)
 {
     static const WCHAR FileOpenNoIntegrationW[] = L"Software\\Wine\\FileOpenNoIntegration\\";
     WCHAR key_path[MAX_PATH];
@@ -2100,11 +2103,11 @@ static BOOL on_exclude_list(const WCHAR *extension, const WCHAR *command)
     DWORD len = ARRAY_SIZE(program_name);
     DWORD i = 0;
 
-    if (ARRAY_SIZE(FileOpenNoIntegrationW) + lstrlenW(extension) > ARRAY_SIZE(key_path))
+    if (ARRAY_SIZE(FileOpenNoIntegrationW) + lstrlenW(win_type) > ARRAY_SIZE(key_path))
         return FALSE;
 
     lstrcpyW(key_path, FileOpenNoIntegrationW);
-    lstrcatW(key_path, extension);
+    lstrcatW(key_path, win_type);
 
     if (RegOpenKeyExW(HKEY_CURRENT_USER, key_path, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
         return FALSE;
@@ -2134,15 +2137,15 @@ static WCHAR *get_special_mime_type(LPCWSTR extension)
     return NULL;
 }
 
-static BOOL write_freedesktop_association_entry(const WCHAR *desktopPath, const WCHAR *friendlyAppName,
-                                                const WCHAR *mimeType, const WCHAR *progId,
-                                                const WCHAR *openWithIcon)
+static BOOL write_freedesktop_association_entry(const WCHAR *desktopPath, const WCHAR *winType,
+                                                const WCHAR *friendlyAppName, const WCHAR *mimeType,
+                                                const WCHAR *progId, const WCHAR *openWithIcon)
 {
     FILE *desktop;
     const WCHAR *prefix = _wgetenv( L"WINECONFIGDIR" );
 
-    WINE_TRACE("friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
-               wine_dbgstr_w(friendlyAppName), wine_dbgstr_w(mimeType),
+    WINE_TRACE("winType=%s, friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
+               wine_dbgstr_w(winType), wine_dbgstr_w(friendlyAppName), wine_dbgstr_w(mimeType),
                wine_dbgstr_w(progId), wine_dbgstr_w(openWithIcon), wine_dbgstr_w(desktopPath));
 
     desktop = _wfopen( desktopPath, L"wb" );
@@ -2159,11 +2162,15 @@ static BOOL write_freedesktop_association_entry(const WCHAR *desktopPath, const
     if (prefix)
     {
         char *path = wine_get_unix_file_name( prefix );
-        fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start /ProgIDOpen %s %%f\n", path, escape(progId));
+        fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start ", path);
         heap_free( path );
     }
     else
-        fprintf(desktop, "Exec=wine start /ProgIDOpen %s %%f\n", escape(progId));
+        fprintf(desktop, "Exec=wine start ");
+    if (progId) /* file association */
+        fprintf(desktop, "/ProgIDOpen %s %%f\n", escape(progId));
+    else /* protocol association */
+        fprintf(desktop, "%%u\n");
     fprintf(desktop, "NoDisplay=true\n");
     fprintf(desktop, "StartupNotify=true\n");
     if (openWithIcon)
@@ -2188,7 +2195,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic
 
     for (i = 0; ; i++)
     {
-        WCHAR *extensionW = NULL;
+        WCHAR *winTypeW = NULL;
         DWORD size = 1024;
         WCHAR *commandW = NULL;
         WCHAR *executableW = NULL;
@@ -2201,102 +2208,119 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic
         WCHAR *progIdW = NULL;
         WCHAR *mimeProgId = NULL;
         struct rb_string_entry *entry;
+        BOOL is_protocol_type = FALSE;
 
         do
         {
-            heap_free(extensionW);
-            extensionW = xmalloc(size * sizeof(WCHAR));
-            ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, i, extensionW, &size, NULL, NULL, NULL, NULL);
+            heap_free(winTypeW);
+            winTypeW = xmalloc(size * sizeof(WCHAR));
+            ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, i, winTypeW, &size, NULL, NULL, NULL, NULL);
             size *= 2;
         } while (ret == ERROR_MORE_DATA);
 
         if (ret != ERROR_SUCCESS)
         {
-            heap_free(extensionW);
+            heap_free(winTypeW);
             break;
         }
 
-        if (extensionW[0] != '.' || is_extension_banned(extensionW))
+        if (winTypeW[0] != '.')
+        {
+            if (RegGetValueW(HKEY_CLASSES_ROOT, winTypeW, L"URL Protocol", RRF_RT_ANY, NULL, NULL, NULL) == ERROR_SUCCESS)
+                is_protocol_type = TRUE;
+        }
+
+        if (!is_protocol_type && (winTypeW[0] != '.' || is_extension_banned(winTypeW)))
             goto end;
 
-        commandW = assoc_query(ASSOCSTR_COMMAND, extensionW, L"open");
+        commandW = assoc_query(ASSOCSTR_COMMAND, winTypeW, L"open");
         if (commandW == NULL)
             /* no command => no application is associated */
             goto end;
 
-        if (on_exclude_list(extensionW, commandW))
+        if (on_exclude_list(winTypeW, commandW))
             /* command is on the exclude list => desktop integration is not desirable */
             goto end;
 
-        wcslwr(extensionW);
-        friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL);
+        wcslwr(winTypeW);
+        friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, winTypeW, NULL);
 
-        iconW = assoc_query(ASSOCSTR_DEFAULTICON, extensionW, NULL);
+        iconW = assoc_query(ASSOCSTR_DEFAULTICON, winTypeW, NULL);
 
-        mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, extensionW);
-
-        if (mimeType == NULL)
+        if (is_protocol_type)
         {
-            contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL);
-            if (contentTypeW != NULL && wcschr(contentTypeW, '/'))
-                mimeType = xwcsdup(wcslwr(contentTypeW));
-            else if (!(mimeType = get_special_mime_type(extensionW)))
-                mimeType = heap_wprintf(L"application/x-wine-extension-%s", &extensionW[1]);
+            mimeType = heap_wprintf(L"x-scheme-handler/%s", winTypeW);
+        }
+        else
+        {
+            mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, winTypeW);
 
-            /* GNOME seems to ignore the <icon> tag in MIME packages,
-             * and the default name is more intuitive anyway.
-             */
-            if (iconW)
+            if (mimeType == NULL)
             {
-                WCHAR *flattened_mime = slashes_to_minuses(mimeType);
-                int index = 0;
-                WCHAR *comma = wcsrchr(iconW, ',');
-                if (comma)
+                contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, winTypeW, NULL);
+                if (contentTypeW != NULL && wcschr(contentTypeW, '/'))
+                    mimeType = xwcsdup(wcslwr(contentTypeW));
+                else if (!(mimeType = get_special_mime_type(winTypeW)))
+                    mimeType = heap_wprintf(L"application/x-wine-extension-%s", &winTypeW[1]);
+
+                /* GNOME seems to ignore the <icon> tag in MIME packages,
+                 * and the default name is more intuitive anyway.
+                 */
+                if (iconW)
                 {
-                    *comma = 0;
-                    index = wcstol(comma + 1, NULL, 10);
+                    WCHAR *flattened_mime = slashes_to_minuses(mimeType);
+                    int index = 0;
+                    WCHAR *comma = wcsrchr(iconW, ',');
+                    if (comma)
+                    {
+                        *comma = 0;
+                        index = wcstol(comma + 1, NULL, 10);
+                    }
+                    extract_icon(iconW, index, flattened_mime, FALSE);
+                    heap_free(flattened_mime);
                 }
-                extract_icon(iconW, index, flattened_mime, FALSE);
-                heap_free(flattened_mime);
+
+                write_freedesktop_mime_type_entry(packages_dir, winTypeW, mimeType, friendlyDocNameW);
+                hasChanged = TRUE;
             }
 
-            write_freedesktop_mime_type_entry(packages_dir, extensionW, mimeType, friendlyDocNameW);
-            hasChanged = TRUE;
+            progIdW = reg_get_valW(HKEY_CLASSES_ROOT, winTypeW, NULL);
+            if (!progIdW) goto end; /* no progID => not a file type association */
+
+            /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */
+            mimeProgId = heap_wprintf(L"%s=>%s", mimeType, progIdW);
+            if (wine_rb_get(&mimeProgidTree, mimeProgId))
+            {
+                heap_free(mimeProgId);
+                goto end;
+            }
+            entry = xmalloc(sizeof(struct rb_string_entry));
+            entry->string = mimeProgId;
+            if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry))
+            {
+                WINE_ERR("error updating rb tree\n");
+                goto end;
+            }
         }
 
-        executableW = assoc_query(ASSOCSTR_EXECUTABLE, extensionW, L"open");
+        executableW = assoc_query(ASSOCSTR_EXECUTABLE, winTypeW, L"open");
         if (executableW)
             openWithIcon = compute_native_identifier(0, executableW, NULL);
 
-        friendlyAppName = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, L"open");
+        friendlyAppName = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, winTypeW, L"open");
         if (!friendlyAppName) friendlyAppName = L"A Wine application";
 
-        progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL);
-        if (!progIdW) goto end; /* no progID => not a file type association */
-
-        /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */
-        mimeProgId = heap_wprintf(L"%s=>%s", mimeType, progIdW);
-        if (wine_rb_get(&mimeProgidTree, mimeProgId))
+        if (has_association_changed(winTypeW, mimeType, progIdW, friendlyAppName, openWithIcon))
         {
-            heap_free(mimeProgId);
-            goto end;
-        }
-        entry = xmalloc(sizeof(struct rb_string_entry));
-        entry->string = mimeProgId;
-        if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry))
-        {
-            WINE_ERR("error updating rb tree\n");
-            goto end;
-        }
-
-        if (has_association_changed(extensionW, mimeType, progIdW, friendlyAppName, openWithIcon))
-        {
-            WCHAR *desktopPath = heap_wprintf(L"%s\\wine-extension-%s.desktop",
-                                              applications_dir, extensionW + 1 );
-            if (write_freedesktop_association_entry(desktopPath, friendlyAppName, mimeType, progIdW, openWithIcon))
+            WCHAR *desktopPath;
+            if (is_protocol_type)
+                desktopPath = heap_wprintf(L"%s\\wine-protocol-%s.desktop", applications_dir, winTypeW);
+            else
+                desktopPath = heap_wprintf(L"%s\\wine-extension-%s.desktop", applications_dir, winTypeW+1);
+            if (write_freedesktop_association_entry(desktopPath, winTypeW, friendlyAppName, mimeType, progIdW, openWithIcon))
             {
                 hasChanged = TRUE;
-                update_association(extensionW, mimeType, progIdW, friendlyAppName, desktopPath, openWithIcon);
+                update_association(winTypeW, mimeType, progIdW, friendlyAppName, desktopPath, openWithIcon);
             }
             heap_free(desktopPath);
         }
@@ -2312,7 +2336,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic
         heap_free(contentTypeW);
         heap_free(mimeType);
         heap_free(progIdW);
-        heap_free(extensionW);
+        heap_free(winTypeW);
     }
 
     wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL);
-- 
2.33.0




More information about the wine-devel mailing list