From: Alex Henrie alexhenrie24@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22904 --- programs/winemenubuilder/winemenubuilder.c | 152 ++++++++++++--------- 1 file changed, 88 insertions(+), 64 deletions(-)
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index ae657d87bb1..46afba5e991 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -1833,10 +1833,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)) @@ -1880,7 +1883,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) @@ -2043,11 +2046,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) @@ -2075,12 +2082,19 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic
for (i = 0; ; i++) { - WCHAR *extensionW; + WCHAR *winTypeW; + BOOL isProtocolType = FALSE;
- if (!(extensionW = reg_enum_keyW(HKEY_CLASSES_ROOT, i))) + if (!(winTypeW = reg_enum_keyW(HKEY_CLASSES_ROOT, i))) 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) + isProtocolType = TRUE; + } + + if (isProtocolType || (winTypeW[0] == '.' && !is_extension_banned(winTypeW))) { WCHAR *commandW = NULL; WCHAR *executableW = NULL; @@ -2094,7 +2108,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic WCHAR *mimeProgId = NULL; struct rb_string_entry *entry;
- 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; @@ -2103,78 +2117,88 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic /* command is on the exclude list => desktop integration is not desirable */ goto end;
- wcslwr(extensionW); - friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL); + iconW = assoc_query(ASSOCSTR_DEFAULTICON, winTypeW, NULL);
- iconW = assoc_query(ASSOCSTR_DEFAULTICON, extensionW, NULL); + if (isProtocolType) + { + mimeType = heap_wprintf(L"x-scheme-handler/%s", winTypeW); + } + else + { + wcslwr(winTypeW); + friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, winTypeW, NULL);
- contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL); - if (contentTypeW) - wcslwr(contentTypeW); + contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, winTypeW, NULL); + if (contentTypeW) + wcslwr(contentTypeW);
- mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, extensionW); + mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, winTypeW);
- if (mimeType == NULL) - { - if (contentTypeW != NULL && wcschr(contentTypeW, '/')) - mimeType = xwcsdup(contentTypeW); - else if (!(mimeType = get_special_mime_type(extensionW))) - mimeType = heap_wprintf(L"application/x-wine-extension-%s", &extensionW[1]); - - /* 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) + if (contentTypeW != NULL && wcschr(contentTypeW, '/')) + mimeType = xwcsdup(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 ); + WCHAR *desktopPath; + if (isProtocolType) + 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, 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); } @@ -2191,7 +2215,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic heap_free(mimeType); heap_free(progIdW); } - heap_free(extensionW); + heap_free(winTypeW); }
wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL);
Doesn't this look too invasive and dangerous? Windows apps just love registering handles for anything. IMO there should be an explicit confirmation somewhere if user wants that.
On Thu May 4 02:01:31 2023 +0000, Paul Gofman wrote:
Doesn't this look too invasive and dangerous? Windows apps just love registering handles for anything. IMO there should be an explicit confirmation somewhere if user wants that.
Firefox and Chrome both prompt for confirmation before opening a link in an external program. Associating applications with custom protocols is actually less dangerous than associating applications with file types (which Wine has been doing for the past 15 years), because the user does have to explicitly confirm that they want to use the program to open the link.
The big motivation for this patch is that there are certain games/programs (like Roblox) that depend on this kind of web browser integration. Maybe someone could get Roblox working in Wine Internet Explorer, but the user experience would be much much worse than in a native web browser.
This patch also doesn't change the fact that Wine has internally supported protocol associations for a very long time, it just hasn't advertised those associations to the host desktop environment. The one case I can think of where it might be annoying is if you install a web browser in Wine then it would appear as an option in the host desktop environment for opening http and https links. However, if you installed a web browser in Wine it's probably because you liked that browser better than your native web browser, so I think it's reasonable to offer desktop integration for it. (By the way, Wine Internet Explorer is already excluded from desktop integration by default.)
This is the changes I would suggest:
1. Adding a `is_protocol_banned` function and adding the `file` protocol to the blacklist. 2. Adding a registry key to disable this new behavior (`winecfg` support?)
This is the code to detect if associations are enabled: ```c static BOOL associations_enabled(void) { BOOL ret = TRUE; HKEY hkey; BYTE buf[32]; DWORD len;
if ((hkey = open_associations_reg_key())) { len = sizeof(buf); if (!RegQueryValueExA(hkey, "Enable", NULL, NULL, buf, &len)) ret = IS_OPTION_TRUE(buf[0]); RegCloseKey( hkey ); }
return ret; } ```
Maybe making a copy of it called `protocol_scheme_enabled` and using `ProtocolSheme`/`ProtocolShemeEnabled` as registry key. (Pick your favorite)
Also I saw that all registry keys related to `winemenubuilder` are missing in the official doc: https://wiki.winehq.org/Useful_Registry_Keys