Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22904
-- v2: winecfg: Mention protocol associations. winemenubuilder: Create .desktop files for programs that open URIs.
From: Alex Henrie alexhenrie24@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22904 --- programs/winemenubuilder/winemenubuilder.c | 163 ++++++++++++--------- 1 file changed, 95 insertions(+), 68 deletions(-)
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index ae657d87bb1..5c3cbd7a80b 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) @@ -1964,12 +1967,15 @@ static BOOL write_freedesktop_mime_type_entry(const WCHAR *packages_dir, const W return ret; }
-static BOOL is_extension_banned(LPCWSTR extension) +static BOOL is_type_banned(const WCHAR *win_type) { /* These are managed through external tools like wine.desktop, to evade malware created file type associations */ - if (!wcsicmp(extension, L".com") || - !wcsicmp(extension, L".exe") || - !wcsicmp(extension, L".msi")) + if (!wcsicmp(win_type, L".com") || + !wcsicmp(win_type, L".exe") || + !wcsicmp(win_type, L".msi")) + return TRUE; + /* Windows programs can't open Unix file URIs */ + if (!wcsicmp(win_type, L"file")) return TRUE; return FALSE; } @@ -2043,11 +2049,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 +2085,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 ((winTypeW[0] == '.' || isProtocolType) && !is_type_banned(winTypeW)) { WCHAR *commandW = NULL; WCHAR *executableW = NULL; @@ -2094,7 +2111,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 +2120,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 +2218,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);
From: Alex Henrie alexhenrie24@gmail.com
--- programs/winecfg/winecfg.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc index b2b51ffbf70..48fe0b9f9f0 100644 --- a/programs/winecfg/winecfg.rc +++ b/programs/winecfg/winecfg.rc @@ -307,7 +307,7 @@ BEGIN CONTROL "",IDC_SYSPARAM_SIZE_UD,UPDOWN_CLASSA,UDS_SETBUDDYINT | UDS_ALIGNRIGHT | WS_DISABLED, 185,75,15,13
GROUPBOX "MIME types",IDC_STATIC,8,95,244,23 - CONTROL "Manage file &associations",IDC_ENABLE_FILE_ASSOCIATIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,105,230,10 + CONTROL "Manage file and protocol &associations",IDC_ENABLE_FILE_ASSOCIATIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,105,230,10
PUSHBUTTON "&Font...",IDC_SYSPARAM_FONT,190,75,55,13,WS_DISABLED GROUPBOX "Folders",IDC_STATIC,8,120,244,94
On Fri May 5 02:25:59 2023 +0000, Loïc Rebmeister wrote:
This is the changes I would suggest:
- 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:
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
Hi Loïc, thanks for the feedback. You raise some good points.
I agree that Wine shouldn't advertise `file:` URI handlers to the desktop environment, because Windows programs can't use Unix file paths. I just pushed a new patch that adds the `file` "protocol" to the blacklist.
I'm not convinced that we need another registry option to disable desktop integration for protocols. We already have `HKCU\Software\Wine\FileOpenAssociations\Exclude` to disable associations for a particular program and `HKCU\Software\Wine\FileOpenAssociations\Enable` to disable associations altogether. I don't see a benefit to treating protocol associations differently than file associations. Not many Windows programs use protocol associations in the first place. However, we could add such an exclusion feature later if there turns out to be a lot of demand for it. For clarity, I did just add a second commit to this merge request that changes the "Manage file associations" checkbox in winecfg to say "Manage file and protocol associations" instead.
https://wiki.winehq.org/ is a wiki. When you see something missing, please add it!
I agree that Wine shouldn't advertise `file:` URI handlers to the desktop environment, because Windows programs can't use Unix file paths. I just pushed a new patch that adds the `file` "protocol" to the blacklist.
We may not want to translate protocol handlers for file: for other reasons, but it's worth pointing out that Wine actually does allow using Unix paths. You can try it yourself by opening cmd and running `explorer /some/unix/path/`. [Note that this translation isn't specific to cmd or anything; it's done at the ntdll level.]