/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Ian McInerney * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void KIPLATFORM::ENV::Init() { // No tasks for this platform } bool KIPLATFORM::ENV::MoveToTrash( const wxString& aPath, wxString& aError ) { // The filename field must be a double-null terminated string wxString temp = aPath + '\0'; SHFILEOPSTRUCT fileOp; ::ZeroMemory( &fileOp, sizeof( fileOp ) ); fileOp.hwnd = nullptr; // Set to null since there is no progress dialog fileOp.wFunc = FO_DELETE; fileOp.pFrom = temp.c_str(); fileOp.pTo = nullptr; // Set to to NULL since we aren't moving the file fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT; int eVal = SHFileOperation( &fileOp ); if( eVal != 0 ) { aError = wxString::Format( _( "Error code: %d" ), eVal ); return false; } return true; } bool KIPLATFORM::ENV::IsNetworkPath( const wxString& aPath ) { return ::PathIsNetworkPathW( aPath.wc_str() ); } wxString KIPLATFORM::ENV::GetDocumentsPath() { // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get() // complains about not existing app. so use a dummy app if( wxTheApp == nullptr ) { wxApp dummy; return wxStandardPaths::Get().GetDocumentsDir(); } return wxStandardPaths::Get().GetDocumentsDir(); } wxString KIPLATFORM::ENV::GetUserConfigPath() { // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get() // complains about not existing app. so use a dummy app if( wxTheApp == nullptr ) { wxApp dummy; return wxStandardPaths::Get().GetUserConfigDir(); } return wxStandardPaths::Get().GetUserConfigDir(); } wxString KIPLATFORM::ENV::GetUserDataPath() { // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get() // complains about not existing app. so use a dummy app if( wxTheApp == nullptr ) { wxApp dummy; return wxStandardPaths::Get().GetUserDataDir(); } return wxStandardPaths::Get().GetUserDataDir(); } wxString KIPLATFORM::ENV::GetUserLocalDataPath() { // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get() // complains about not existing app. so use a dummy app if( wxTheApp == nullptr ) { wxApp dummy; return wxStandardPaths::Get().GetUserLocalDataDir(); } return wxStandardPaths::Get().GetUserLocalDataDir(); } wxString KIPLATFORM::ENV::GetUserCachePath() { // Unfortunately AppData/Local is the closest analog to "Cache" directories of other platforms // Make sure we don't include the "appinfo" (appended app name) // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get() // complains about not existing app. so use a dummy app if( wxTheApp == nullptr ) { wxApp dummy; wxStandardPaths::Get().UseAppInfo( wxStandardPaths::AppInfo_None ); return wxStandardPaths::Get().GetUserLocalDataDir(); } wxStandardPaths::Get().UseAppInfo( wxStandardPaths::AppInfo_None ); return wxStandardPaths::Get().GetUserLocalDataDir(); } bool KIPLATFORM::ENV::GetSystemProxyConfig( const wxString& aURL, PROXY_CONFIG& aCfg ) { // Original source from Microsoft sample (public domain) // https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/WinhttpProxy/cpp/GetProxy.cpp#L844 bool autoProxyDetect = false; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig = { 0 }; WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions = { 0 }; WINHTTP_PROXY_INFO autoProxyInfo = { 0 }; HINTERNET proxyResolveSession = NULL; bool success = false; wxURI uri( aURL ); LPWSTR proxyStr = NULL; LPWSTR bypassProxyStr = NULL; if( WinHttpGetIEProxyConfigForCurrentUser( &ieProxyConfig ) ) { // welcome to the wonderful world of IE // we use the ie config simply to handle it off to the other win32 api if( ieProxyConfig.fAutoDetect ) { autoProxyDetect = true; } if( ieProxyConfig.lpszAutoConfigUrl != NULL ) { autoProxyDetect = true; autoProxyOptions.lpszAutoConfigUrl = ieProxyConfig.lpszAutoConfigUrl; } } else if( GetLastError() == ERROR_FILE_NOT_FOUND ) { // this is the only error code where we want to continue attempting to find a proxy autoProxyDetect = true; } if( autoProxyDetect ) { proxyResolveSession = WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC ); if( proxyResolveSession ) { // either we use the ie url or we set the auto detect mode if( autoProxyOptions.lpszAutoConfigUrl != NULL ) { autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; } else { autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; } // dont do auto logon at first, this allows windows to use an cache // per https://docs.microsoft.com/en-us/windows/win32/winhttp/autoproxy-cache autoProxyOptions.fAutoLogonIfChallenged = FALSE; autoProxyDetect = WinHttpGetProxyForUrl( proxyResolveSession, aURL.c_str(), &autoProxyOptions, &autoProxyInfo ); if( !autoProxyDetect && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE ) { autoProxyOptions.fAutoLogonIfChallenged = TRUE; // try again with auto login now autoProxyDetect = WinHttpGetProxyForUrl( proxyResolveSession, aURL.c_str(), &autoProxyOptions, &autoProxyInfo ); } if( autoProxyDetect ) { if( autoProxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY ) { proxyStr = autoProxyInfo.lpszProxy; bypassProxyStr = autoProxyInfo.lpszProxyBypass; } } WinHttpCloseHandle( proxyResolveSession ); } } if( !autoProxyDetect && ieProxyConfig.lpszProxy != NULL ) { proxyStr = ieProxyConfig.lpszProxy; bypassProxyStr = ieProxyConfig.lpszProxyBypass; } bool bypassed = false; if( bypassProxyStr != NULL ) { wxStringTokenizer tokenizer( bypassProxyStr, wxT( ";" ) ); while( tokenizer.HasMoreTokens() ) { wxString host = tokenizer.GetNextToken(); if( host == uri.GetServer() ) { // the given url has a host in the proxy bypass list return false; } // is a special case that says all local sites bypass // the windows way for considering local is any host without periods in the name that would imply // some non-internal dns resolution if( host == "" ) { if( !uri.GetServer().Contains( "." ) ) { // great its a local uri that is bypassed bypassed = true; break; } } } } if( !bypassed && proxyStr != NULL ) { // proxyStr can be in the following format per MSDN //([=]["://"][":"]) //and separated by semicolons or whitespace wxStringTokenizer tokenizer( proxyStr, wxT( "; \t" ) ); while( tokenizer.HasMoreTokens() ) { wxString entry = tokenizer.GetNextToken(); // deal with the [=] part, which may or may not exist if( entry.Contains( "=" ) ) { wxString scheme = entry.BeforeFirst( '=' ).Lower(); entry = entry.AfterFirst( '=' ); // skip processing if the scheme doesnt match if( scheme != uri.GetScheme().Lower() ) { continue; } // we continue with the [=] stripped off if we matched } // is the entry left not empty? we just take the first result // : and :: are also special cases we want to ignore if( entry != "" && entry != ":" && entry != "::" ) { aCfg.host = entry; success = true; break; } } } // We have to clean up the strings the win32 api returned if( autoProxyInfo.lpszProxy ) { GlobalFree( autoProxyInfo.lpszProxy ); autoProxyInfo.lpszProxy = NULL; } if( autoProxyInfo.lpszProxyBypass ) { GlobalFree( autoProxyInfo.lpszProxyBypass ); autoProxyInfo.lpszProxyBypass = NULL; } if( ieProxyConfig.lpszAutoConfigUrl != NULL ) { GlobalFree( ieProxyConfig.lpszAutoConfigUrl ); ieProxyConfig.lpszAutoConfigUrl = NULL; } if( ieProxyConfig.lpszProxy != NULL ) { GlobalFree( ieProxyConfig.lpszProxy ); ieProxyConfig.lpszProxy = NULL; } if( ieProxyConfig.lpszProxyBypass != NULL ) { GlobalFree( ieProxyConfig.lpszProxyBypass ); ieProxyConfig.lpszProxyBypass = NULL; } return success; } bool KIPLATFORM::ENV::VerifyFileSignature( const wxString& aPath ) { WINTRUST_FILE_INFO fileData; memset( &fileData, 0, sizeof( fileData ) ); fileData.cbStruct = sizeof( WINTRUST_FILE_INFO ); fileData.pcwszFilePath = aPath.wc_str(); // verifies entire certificate chain GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2; WINTRUST_DATA trustData; memset( &trustData, 0, sizeof( trustData ) ); trustData.cbStruct = sizeof( trustData ); trustData.dwUIChoice = WTD_UI_NONE; // revocation checking incurs latency penalities due to need for online queries trustData.fdwRevocationChecks = WTD_REVOKE_NONE; trustData.dwUnionChoice = WTD_CHOICE_FILE; trustData.dwStateAction = WTD_STATEACTION_VERIFY; trustData.pFile = &fileData; bool verified = false; LONG status = WinVerifyTrust( NULL, &policy, &trustData ); verified = ( status == ERROR_SUCCESS ); // Cleanup/release (yes its weird looking) trustData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust( NULL, &policy, &trustData ); return verified; }