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 if( bypassProxyStr != NULL )
268 {
269 wxStringTokenizer tokenizer( bypassProxyStr, wxT( ";" ) );
270
271 while( tokenizer.HasMoreTokens() )
272 {
273 wxString host = tokenizer.GetNextToken();
274
275 if( host == uri.GetServer() )
276 {
277 // the given url has a host in the proxy bypass list
278 return false;
279 }
280
281 // <local> is a special case that says all local sites bypass
282 // the windows way for considering local is any host without periods in the name that would imply
283 // some non-internal dns resolution
284 if( host == "<local>" )
285 {
286 if( !uri.GetServer().Contains( "." ) )
287 {
288 // great its a local uri that is bypassed
289 bypassed = true;
290 break;
291 }
292 }
293 }
294 }
295
296 if( !bypassed && proxyStr != NULL )
297 {
298 // proxyStr can be in the following format per MSDN
299 //([<scheme>=][<scheme>"://"]<server>[":"<port>])
300 //and separated by semicolons or whitespace
301 wxStringTokenizer tokenizer( proxyStr, wxT( "; \t" ) );
302
303 while( tokenizer.HasMoreTokens() )
304 {
305 wxString entry = tokenizer.GetNextToken();
306
307 // deal with the [<scheme>=] part, which may or may not exist
308 if( entry.Contains( "=" ) )
309 {
310 wxString scheme = entry.BeforeFirst( '=' ).Lower();
311 entry = entry.AfterFirst( '=' );
312
313 // skip processing if the scheme doesnt match
314 if( scheme != uri.GetScheme().Lower() )
315 {
316 continue;
317 }
318
319 // we continue with the [<scheme>=] stripped off if we matched
320 }
321
322 // is the entry left not empty? we just take the first result
323 // : and :: are also special cases we want to ignore
324 if( entry != "" && entry != ":" && entry != "::" )
325 {
326 aCfg.host = entry;
327 success = true;
328 break;
329 }
330 }
331 }
332
333
334 // We have to clean up the strings the win32 api returned
335 if( autoProxyInfo.lpszProxy )
336 {
337 GlobalFree( autoProxyInfo.lpszProxy );
338 autoProxyInfo.lpszProxy = NULL;
339 }
340
341 if( autoProxyInfo.lpszProxyBypass )
342 {
343 GlobalFree( autoProxyInfo.lpszProxyBypass );
344 autoProxyInfo.lpszProxyBypass = NULL;
345 }
346
347 if( ieProxyConfig.lpszAutoConfigUrl != NULL )
348 {
349 GlobalFree( ieProxyConfig.lpszAutoConfigUrl );
350 ieProxyConfig.lpszAutoConfigUrl = NULL;
351 }
352
353 if( ieProxyConfig.lpszProxy != NULL )
354 {
355 GlobalFree( ieProxyConfig.lpszProxy );
356 ieProxyConfig.lpszProxy = NULL;
357 }
358
359 if( ieProxyConfig.lpszProxyBypass != NULL )
360 {
361 GlobalFree( ieProxyConfig.lpszProxyBypass );
362 ieProxyConfig.lpszProxyBypass = NULL;
363 }
364
365 return success;
366}
367
368
369bool KIPLATFORM::ENV::VerifyFileSignature( const wxString& aPath )
370{
371 WINTRUST_FILE_INFO fileData;
372 memset( &fileData, 0, sizeof( fileData ) );
373 fileData.cbStruct = sizeof( WINTRUST_FILE_INFO );
374 fileData.pcwszFilePath = aPath.wc_str();
375
376 // verifies entire certificate chain
377 GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
378
379 WINTRUST_DATA trustData;
380 memset( &trustData, 0, sizeof( trustData ) );
381
382 trustData.cbStruct = sizeof( trustData );
383 trustData.dwUIChoice = WTD_UI_NONE;
384 // revocation checking incurs latency penalities due to need for online queries
385 trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
386 trustData.dwUnionChoice = WTD_CHOICE_FILE;
387 trustData.dwStateAction = WTD_STATEACTION_VERIFY;
388 trustData.pFile = &fileData;
389
390
391 bool verified = false;
392 LONG status = WinVerifyTrust( NULL, &policy, &trustData );
393
394 verified = ( status == ERROR_SUCCESS );
395
396 // Cleanup/release (yes its weird looking)
397 trustData.dwStateAction = WTD_STATEACTION_CLOSE;
398 WinVerifyTrust( NULL, &policy, &trustData );
399
400 return verified;
401}
402
403
405{
406 // The application model id allows for taskbar grouping
407 // However, be warned, this cannot be too unique like per-process
408 // Because longer scope Windows features, such as "Pin to Taskbar"
409 // on a running application, depend on this being consistent.
410 std::vector<wxString> modelIdComponents;
411 modelIdComponents.push_back( wxS( "Kicad" ) );
412 modelIdComponents.push_back( wxS( "Kicad" ) );
413 modelIdComponents.push_back( wxTheApp->GetAppName() );
414 modelIdComponents.push_back( KICAD_MAJOR_MINOR_VERSION );
415
416 wxString modelId;
417 for( const auto& str : modelIdComponents )
418 {
419 modelId += str;
420 modelId += wxS( "." );
421 }
422
423 modelId.RemoveLast(); // remove trailing dot
424 modelId.Replace( wxS( " " ), wxS( "_" ) ); // remove spaces sanity
425
426 // the other limitation is 127 characters but we arent trying to hit that limit yet
427
428 return modelId;
429}
430
431
432void KIPLATFORM::ENV::SetAppDetailsForWindow( wxWindow* aWindow, const wxString& aRelaunchCommand,
433 const wxString& aRelaunchDisplayName )
434{
435 IPropertyStore* pps;
436 HRESULT hr = ::SHGetPropertyStoreForWindow( aWindow->GetHWND(), IID_PPV_ARGS( &pps ) );
437 if( SUCCEEDED( hr ) )
438 {
439 PROPVARIANT pv;
440
441 // This is required for any the other properties to actually work
442 hr = ::InitPropVariantFromString( GetAppUserModelId().wc_str(), &pv );
443
444 if( SUCCEEDED( hr ) )
445 {
446 hr = pps->SetValue( PKEY_AppUserModel_ID, pv );
447 PropVariantClear( &pv );
448 }
449
450
451 if( !aRelaunchCommand.empty() )
452 {
453 hr = ::InitPropVariantFromString( aRelaunchCommand.wc_str(), &pv );
454 }
455 else
456 {
457 // empty var
458 ::PropVariantInit( &pv );
459 }
460
461 if( SUCCEEDED( hr ) )
462 {
463 hr = pps->SetValue( PKEY_AppUserModel_RelaunchCommand, pv );
464 PropVariantClear( &pv );
465 }
466
467 if( !aRelaunchDisplayName.empty() )
468 {
469 hr = ::InitPropVariantFromString( aRelaunchDisplayName.wc_str(), &pv );
470 }
471 else
472 {
473 // empty var
474 ::PropVariantInit( &pv );
475 }
476
477 if( SUCCEEDED( hr ) )
478 {
479 hr = pps->SetValue( PKEY_AppUserModel_RelaunchDisplayNameResource, pv );
480 PropVariantClear( &pv );
481 }
482
483 pps->Release();
484 }
485}
486
487
489{
490 return ::GetCommandLine();
491}
492
493
494void KIPLATFORM::ENV::AddToRecentDocs( const wxString& aPath )
495{
496 IShellItem* psi = nullptr;
497 HRESULT hr = SHCreateItemFromParsingName( aPath.wc_str(), NULL, IID_PPV_ARGS( &psi ) );
498
499 if( SUCCEEDED( hr ) )
500 {
501 wxString appID = GetAppUserModelId();
502 SHARDAPPIDINFO info;
503 info.psi = psi;
504 info.pszAppID = appID.wc_str();
505 ::SHAddToRecentDocs( SHARD_APPIDINFO, &info );
506
507 psi->Release();
508 }
509
510 ::SHAddToRecentDocs( SHARD_PATHW, aPath.wc_str() );
511}
#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