383 lines
12 KiB
C++
383 lines
12 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2020 Ian McInerney <Ian.S.McInerney at ieee.org>
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <kiplatform/environment.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/filename.h>
|
|
#include <wx/stdpaths.h>
|
|
#include <wx/string.h>
|
|
#include <wx/tokenzr.h>
|
|
#include <wx/app.h>
|
|
#include <wx/uri.h>
|
|
|
|
#include <Windows.h>
|
|
#include <shellapi.h>
|
|
#include <shlwapi.h>
|
|
#include <winhttp.h>
|
|
|
|
#include <Softpub.h>
|
|
#include <wincrypt.h>
|
|
#include <wintrust.h>
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
// <local> 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 == "<local>" )
|
|
{
|
|
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
|
|
//([<scheme>=][<scheme>"://"]<server>[":"<port>])
|
|
//and separated by semicolons or whitespace
|
|
wxStringTokenizer tokenizer( proxyStr, wxT( "; \t" ) );
|
|
|
|
while( tokenizer.HasMoreTokens() )
|
|
{
|
|
wxString entry = tokenizer.GetNextToken();
|
|
|
|
// deal with the [<scheme>=] 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 [<scheme>=] 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;
|
|
} |