KiCad PCB EDA Suite
Loading...
Searching...
No Matches
windows/environment.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Ian McInerney <Ian.S.McInerney at ieee.org>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
22#include <wx/intl.h>
23#include <wx/filename.h>
24#include <wx/stdpaths.h>
25#include <wx/string.h>
26#include <wx/tokenzr.h>
27#include <wx/app.h>
28#include <wx/uri.h>
29#include <wx/window.h>
30
31#include <windows.h>
32#include <shellapi.h>
33#include <shlwapi.h>
34#include <propkey.h>
35#include <propvarutil.h>
36#if defined( __MINGW32__ )
37 #include <shobjidl.h>
38#else
39 #include <shobjidl_core.h>
40#endif
41#include <winhttp.h>
42
43#include <softpub.h>
44
45#if defined( __MINGW32__ )
46 #include <shlobj.h>
47#else
48 #include <shlobj_core.h>
49#endif
50
51#include <wincrypt.h>
52#include <wintrust.h>
53
54#define INCLUDE_KICAD_VERSION // fight me
55#include <kicad_build_version.h>
56
57
59{
60 ::SetCurrentProcessExplicitAppUserModelID( GetAppUserModelId().wc_str() );
61}
62
63
64bool KIPLATFORM::ENV::MoveToTrash( const wxString& aPath, wxString& aError )
65{
66 // The filename field must be a double-null terminated string
67 wxString temp = aPath + '\0';
68
69 SHFILEOPSTRUCT fileOp;
70 ::ZeroMemory( &fileOp, sizeof( fileOp ) );
71
72 fileOp.hwnd = nullptr; // Set to null since there is no progress dialog
73 fileOp.wFunc = FO_DELETE;
74 fileOp.pFrom = temp.c_str();
75 fileOp.pTo = nullptr; // Set to to NULL since we aren't moving the file
76 fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT;
77
78 int eVal = SHFileOperation( &fileOp );
79
80 if( eVal != 0 )
81 {
82 aError = wxString::Format( _( "Error code: %d" ), eVal );
83 return false;
84 }
85
86 return true;
87}
88
89
90bool KIPLATFORM::ENV::IsNetworkPath( const wxString& aPath )
91{
92 return ::PathIsNetworkPathW( aPath.wc_str() );
93}
94
95
97{
98 // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get()
99 // complains about not existing app. so use a dummy app
100 if( wxTheApp == nullptr )
101 {
102 wxApp dummy;
103 return wxStandardPaths::Get().GetDocumentsDir();
104 }
105
106 return wxStandardPaths::Get().GetDocumentsDir();
107}
108
109
111{
112 // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get()
113 // complains about not existing app. so use a dummy app
114 if( wxTheApp == nullptr )
115 {
116 wxApp dummy;
117 return wxStandardPaths::Get().GetUserConfigDir();
118 }
119
120 return wxStandardPaths::Get().GetUserConfigDir();
121}
122
123
125{
126 // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get()
127 // complains about not existing app. so use a dummy app
128 if( wxTheApp == nullptr )
129 {
130 wxApp dummy;
131 return wxStandardPaths::Get().GetUserDataDir();
132 }
133
134 return wxStandardPaths::Get().GetUserDataDir();
135}
136
137
139{
140 // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get()
141 // complains about not existing app. so use a dummy app
142 if( wxTheApp == nullptr )
143 {
144 wxApp dummy;
145 return wxStandardPaths::Get().GetUserLocalDataDir();
146 }
147
148 return wxStandardPaths::Get().GetUserLocalDataDir();
149}
150
151
153{
154 // Unfortunately AppData/Local is the closest analog to "Cache" directories of other platforms
155
156 // Make sure we don't include the "appinfo" (appended app name)
157
158 // If called by a python script in stand-alone (outside KiCad), wxStandardPaths::Get()
159 // complains about not existing app. so use a dummy app
160 if( wxTheApp == nullptr )
161 {
162 wxApp dummy;
163 wxStandardPaths::Get().UseAppInfo( wxStandardPaths::AppInfo_None );
164
165 return wxStandardPaths::Get().GetUserLocalDataDir();
166 }
167
168 wxStandardPaths::Get().UseAppInfo( wxStandardPaths::AppInfo_None );
169
170 return wxStandardPaths::Get().GetUserLocalDataDir();
171}
172
173
174bool KIPLATFORM::ENV::GetSystemProxyConfig( const wxString& aURL, PROXY_CONFIG& aCfg )
175{
176 // Original source from Microsoft sample (public domain)
177 // https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/WinhttpProxy/cpp/GetProxy.cpp#L844
178 bool autoProxyDetect = false;
179 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig = { 0 };
180 WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions = { 0 };
181 WINHTTP_PROXY_INFO autoProxyInfo = { 0 };
182 HINTERNET proxyResolveSession = NULL;
183 bool success = false;
184
185 wxURI uri( aURL );
186
187 LPWSTR proxyStr = NULL;
188 LPWSTR bypassProxyStr = NULL;
189
190 if( WinHttpGetIEProxyConfigForCurrentUser( &ieProxyConfig ) )
191 {
192 // welcome to the wonderful world of IE
193 // we use the ie config simply to handle it off to the other win32 api
194 if( ieProxyConfig.fAutoDetect )
195 {
196 autoProxyDetect = true;
197 }
198
199 if( ieProxyConfig.lpszAutoConfigUrl != NULL )
200 {
201 autoProxyDetect = true;
202 autoProxyOptions.lpszAutoConfigUrl = ieProxyConfig.lpszAutoConfigUrl;
203 }
204 }
205 else if( GetLastError() == ERROR_FILE_NOT_FOUND )
206 {
207 // this is the only error code where we want to continue attempting to find a proxy
208 autoProxyDetect = true;
209 }
210
211 if( autoProxyDetect )
212 {
213 proxyResolveSession =
214 WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME,
215 WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC );
216
217 if( proxyResolveSession )
218 {
219 // either we use the ie url or we set the auto detect mode
220 if( autoProxyOptions.lpszAutoConfigUrl != NULL )
221 {
222 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
223 }
224 else
225 {
226 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
227 autoProxyOptions.dwAutoDetectFlags =
228 WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
229 }
230
231 // dont do auto logon at first, this allows windows to use an cache
232 // per https://docs.microsoft.com/en-us/windows/win32/winhttp/autoproxy-cache
233 autoProxyOptions.fAutoLogonIfChallenged = FALSE;
234
235 autoProxyDetect = WinHttpGetProxyForUrl( proxyResolveSession, aURL.c_str(),
236 &autoProxyOptions, &autoProxyInfo );
237
238 if( !autoProxyDetect && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE )
239 {
240 autoProxyOptions.fAutoLogonIfChallenged = TRUE;
241
242 // try again with auto login now
243 autoProxyDetect = WinHttpGetProxyForUrl( proxyResolveSession, aURL.c_str(),
244 &autoProxyOptions, &autoProxyInfo );
245 }
246
247 if( autoProxyDetect )
248 {
249 if( autoProxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY )
250 {
251 proxyStr = autoProxyInfo.lpszProxy;
252 bypassProxyStr = autoProxyInfo.lpszProxyBypass;
253 }
254 }
255
256 WinHttpCloseHandle( proxyResolveSession );
257 }
258 }
259
260 if( !autoProxyDetect && ieProxyConfig.lpszProxy != NULL )
261 {
262 proxyStr = ieProxyConfig.lpszProxy;
263 bypassProxyStr = ieProxyConfig.lpszProxyBypass;
264 }
265
266 bool bypassed = false;
267
268 if( bypassProxyStr != NULL )
269 {
270 wxStringTokenizer tokenizer( bypassProxyStr, ";" );
271
272 while( tokenizer.HasMoreTokens() )
273 {
274 wxString host = tokenizer.GetNextToken();
275
276 if( host == uri.GetServer() )
277 {
278 // the given url has a host in the proxy bypass list
279 return false;
280 }
281
282 // <local> is a special case that says all local sites bypass
283 // the windows way for considering local is any host without periods in the name that would imply
284 // some non-internal dns resolution
285 if( host == "<local>" )
286 {
287 if( !uri.GetServer().Contains( "." ) )
288 {
289 // great its a local uri that is bypassed
290 bypassed = true;
291 break;
292 }
293 }
294 }
295 }
296
297 if( !bypassed && proxyStr != NULL )
298 {
299 // proxyStr can be in the following format per MSDN
300 //([<scheme>=][<scheme>"://"]<server>[":"<port>])
301 //and separated by semicolons or whitespace
302 wxStringTokenizer tokenizer( proxyStr, "; \t" );
303
304 while( tokenizer.HasMoreTokens() )
305 {
306 wxString entry = tokenizer.GetNextToken();
307
308 // deal with the [<scheme>=] part, which may or may not exist
309 if( entry.Contains( "=" ) )
310 {
311 wxString scheme = entry.BeforeFirst( '=' ).Lower();
312 entry = entry.AfterFirst( '=' );
313
314 // skip processing if the scheme doesnt match
315 if( scheme != uri.GetScheme().Lower() )
316 {
317 continue;
318 }
319
320 // we continue with the [<scheme>=] stripped off if we matched
321 }
322
323 // is the entry left not empty? we just take the first result
324 // : and :: are also special cases we want to ignore
325 if( entry != "" && entry != ":" && entry != "::" )
326 {
327 aCfg.host = entry;
328 success = true;
329 break;
330 }
331 }
332 }
333
334
335 // We have to clean up the strings the win32 api returned
336 if( autoProxyInfo.lpszProxy )
337 {
338 GlobalFree( autoProxyInfo.lpszProxy );
339 autoProxyInfo.lpszProxy = NULL;
340 }
341
342 if( autoProxyInfo.lpszProxyBypass )
343 {
344 GlobalFree( autoProxyInfo.lpszProxyBypass );
345 autoProxyInfo.lpszProxyBypass = NULL;
346 }
347
348 if( ieProxyConfig.lpszAutoConfigUrl != NULL )
349 {
350 GlobalFree( ieProxyConfig.lpszAutoConfigUrl );
351 ieProxyConfig.lpszAutoConfigUrl = NULL;
352 }
353
354 if( ieProxyConfig.lpszProxy != NULL )
355 {
356 GlobalFree( ieProxyConfig.lpszProxy );
357 ieProxyConfig.lpszProxy = NULL;
358 }
359
360 if( ieProxyConfig.lpszProxyBypass != NULL )
361 {
362 GlobalFree( ieProxyConfig.lpszProxyBypass );
363 ieProxyConfig.lpszProxyBypass = NULL;
364 }
365
366 return success;
367}
368
369
370bool KIPLATFORM::ENV::VerifyFileSignature( const wxString& aPath )
371{
372 WINTRUST_FILE_INFO fileData;
373 memset( &fileData, 0, sizeof( fileData ) );
374 fileData.cbStruct = sizeof( WINTRUST_FILE_INFO );
375 fileData.pcwszFilePath = aPath.wc_str();
376
377 // verifies entire certificate chain
378 GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
379
380 WINTRUST_DATA trustData;
381 memset( &trustData, 0, sizeof( trustData ) );
382
383 trustData.cbStruct = sizeof( trustData );
384 trustData.dwUIChoice = WTD_UI_NONE;
385 // revocation checking incurs latency penalities due to need for online queries
386 trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
387 trustData.dwUnionChoice = WTD_CHOICE_FILE;
388 trustData.dwStateAction = WTD_STATEACTION_VERIFY;
389 trustData.pFile = &fileData;
390
391
392 bool verified = false;
393 LONG status = WinVerifyTrust( NULL, &policy, &trustData );
394
395 verified = ( status == ERROR_SUCCESS );
396
397 // Cleanup/release (yes its weird looking)
398 trustData.dwStateAction = WTD_STATEACTION_CLOSE;
399 WinVerifyTrust( NULL, &policy, &trustData );
400
401 return verified;
402}
403
404
406{
407 // The application model id allows for taskbar grouping
408 // However, be warned, this cannot be too unique like per-process
409 // Because longer scope Windows features, such as "Pin to Taskbar"
410 // on a running application, depend on this being consistent.
411 std::vector<wxString> modelIdComponents;
412 modelIdComponents.push_back( wxS( "Kicad" ) );
413 modelIdComponents.push_back( wxS( "Kicad" ) );
414 modelIdComponents.push_back( wxTheApp->GetAppName() );
415 modelIdComponents.push_back( KICAD_MAJOR_MINOR_VERSION );
416
417 wxString modelId;
418 for( const auto& str : modelIdComponents )
419 {
420 modelId += str;
421 modelId += wxS( "." );
422 }
423
424 modelId.RemoveLast(); // remove trailing dot
425 modelId.Replace( wxS( " " ), wxS( "_" ) ); // remove spaces sanity
426
427 // the other limitation is 127 characters but we arent trying to hit that limit yet
428
429 return modelId;
430}
431
432
433void KIPLATFORM::ENV::SetAppDetailsForWindow( wxWindow* aWindow, const wxString& aRelaunchCommand,
434 const wxString& aRelaunchDisplayName )
435{
436 IPropertyStore* pps;
437 HRESULT hr = ::SHGetPropertyStoreForWindow( aWindow->GetHWND(), IID_PPV_ARGS( &pps ) );
438 if( SUCCEEDED( hr ) )
439 {
440 PROPVARIANT pv;
441
442 // This is required for any the other properties to actually work
443 hr = ::InitPropVariantFromString( GetAppUserModelId().wc_str(), &pv );
444
445 if( SUCCEEDED( hr ) )
446 {
447 hr = pps->SetValue( PKEY_AppUserModel_ID, pv );
448 PropVariantClear( &pv );
449 }
450
451
452 if( !aRelaunchCommand.empty() )
453 {
454 hr = ::InitPropVariantFromString( aRelaunchCommand.wc_str(), &pv );
455 }
456 else
457 {
458 // empty var
459 ::PropVariantInit( &pv );
460 }
461
462 if( SUCCEEDED( hr ) )
463 {
464 hr = pps->SetValue( PKEY_AppUserModel_RelaunchCommand, pv );
465 PropVariantClear( &pv );
466 }
467
468 if( !aRelaunchDisplayName.empty() )
469 {
470 hr = ::InitPropVariantFromString( aRelaunchDisplayName.wc_str(), &pv );
471 }
472 else
473 {
474 // empty var
475 ::PropVariantInit( &pv );
476 }
477
478 if( SUCCEEDED( hr ) )
479 {
480 hr = pps->SetValue( PKEY_AppUserModel_RelaunchDisplayNameResource, pv );
481 PropVariantClear( &pv );
482 }
483
484 pps->Release();
485 }
486}
487
488
490{
491 return ::GetCommandLine();
492}
493
494
495void KIPLATFORM::ENV::AddToRecentDocs( const wxString& aPath )
496{
497 IShellItem* psi = nullptr;
498 HRESULT hr = SHCreateItemFromParsingName( aPath.wc_str(), NULL, IID_PPV_ARGS( &psi ) );
499
500 if( SUCCEEDED( hr ) )
501 {
502 wxString appID = GetAppUserModelId();
503 SHARDAPPIDINFO info;
504 info.psi = psi;
505 info.pszAppID = appID.wc_str();
506 ::SHAddToRecentDocs( SHARD_APPIDINFO, &info );
507
508 psi->Release();
509 }
510
511 ::SHAddToRecentDocs( SHARD_PATHW, aPath.wc_str() );
512}
#define _(s)
bool IsNetworkPath(const wxString &aPath)
Determines if a given path is a network shared file apth On Windows for example, any form of path is ...
void Init()
Perform environment initialization tasks.
wxString GetCommandLineStr()
bool GetSystemProxyConfig(const wxString &aURL, PROXY_CONFIG &aCfg)
Retrieves platform level proxying requirements to reach the given url.
wxString GetUserDataPath()
Retrieves the operating system specific path for a user's data store.
wxString GetDocumentsPath()
Retrieves the operating system specific path for a user's documents.
wxString GetAppUserModelId()
Retrieves the app user model id, a special string used for taskbar grouping on Windows 7 and later.
void SetAppDetailsForWindow(wxWindow *aWindow, const wxString &aRelaunchCommand, const wxString &aRelaunchDisplayName)
Sets the relaunch command for taskbar pins, this is intended for Windows.
void AddToRecentDocs(const wxString &aPath)
bool MoveToTrash(const wxString &aPath, wxString &aError)
Move the specified file/directory to the trash bin/recycle bin.
wxString GetUserLocalDataPath()
Retrieves the operating system specific path for a user's local data store.
wxString GetUserConfigPath()
Retrieves the operating system specific path for a user's configuration store.
wxString GetUserCachePath()
Retrieves the operating system specific path for user's application cache.
bool VerifyFileSignature(const wxString &aPath)
Validates the code signing signature of a given file This is most likely only ever going to be applic...
std::vector< FAB_LAYER_COLOR > dummy