urllaunch.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "Common/URLLaunch.h"
  19. #define FILE_PREFIX L"file://"
  20. ///////////////////////////////////////////////////////////////////////////////
  21. HRESULT MakeEscapedURL( LPWSTR pszInURL, LPWSTR *ppszOutURL )
  22. {
  23. if( ( NULL == pszInURL ) || ( NULL == ppszOutURL ) )
  24. {
  25. return( E_INVALIDARG );
  26. }
  27. //
  28. // Do we need to pre-pend file://?
  29. //
  30. BOOL fNeedFilePrefix = ( 0 == wcsstr( pszInURL, L"://" ) );
  31. //
  32. // Count how many characters need to be escaped
  33. //
  34. LPWSTR pszTemp = pszInURL;
  35. DWORD cEscapees = 0;
  36. while( TRUE )
  37. {
  38. LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
  39. if( NULL == pchToEscape )
  40. {
  41. break;
  42. }
  43. cEscapees++;
  44. pszTemp = pchToEscape + 1;
  45. }
  46. //
  47. // Allocate sufficient outgoing buffer space
  48. //
  49. int cchNeeded = wcslen( pszInURL ) + ( 2 * cEscapees ) + 1;
  50. if( fNeedFilePrefix )
  51. {
  52. cchNeeded += wcslen( FILE_PREFIX );
  53. }
  54. *ppszOutURL = new WCHAR[ cchNeeded ];
  55. if( NULL == *ppszOutURL )
  56. {
  57. return( E_OUTOFMEMORY );
  58. }
  59. //
  60. // Fill in the outgoing escaped buffer
  61. //
  62. pszTemp = pszInURL;
  63. LPWSTR pchNext = *ppszOutURL;
  64. if( fNeedFilePrefix )
  65. {
  66. wcscpy( *ppszOutURL, FILE_PREFIX );
  67. pchNext += wcslen( FILE_PREFIX );
  68. }
  69. while( TRUE )
  70. {
  71. LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
  72. if( NULL == pchToEscape )
  73. {
  74. //
  75. // Copy the rest of the input string and get out
  76. //
  77. wcscpy( pchNext, pszTemp );
  78. break;
  79. }
  80. //
  81. // Copy all characters since the previous escapee
  82. //
  83. int cchToCopy = pchToEscape - pszTemp;
  84. if( cchToCopy > 0 )
  85. {
  86. wcsncpy( pchNext, pszTemp, cchToCopy );
  87. pchNext += cchToCopy;
  88. }
  89. //
  90. // Expand this character into an escape code and move on
  91. //
  92. pchNext += swprintf( pchNext, L"%%%02x", *pchToEscape );
  93. pszTemp = pchToEscape + 1;
  94. }
  95. return( S_OK );
  96. }
  97. ///////////////////////////////////////////////////////////////////////////////
  98. HRESULT GetShellOpenCommand( LPTSTR ptszShellOpenCommand, DWORD cbShellOpenCommand )
  99. {
  100. LONG lResult;
  101. HKEY hKey = NULL;
  102. HKEY hFileKey = NULL;
  103. BOOL fFoundExtensionCommand = FALSE;
  104. do
  105. {
  106. //
  107. // Look for the file type associated with .html files
  108. //
  109. TCHAR szFileType[ MAX_PATH ];
  110. lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( ".html" ), 0, KEY_READ, &hKey );
  111. if( ERROR_SUCCESS != lResult )
  112. {
  113. break;
  114. }
  115. DWORD dwLength = sizeof( szFileType );
  116. lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)szFileType, &dwLength );
  117. if( ERROR_SUCCESS != lResult )
  118. {
  119. break;
  120. }
  121. //
  122. // Find the command for the shell's open verb associated with this file type
  123. //
  124. TCHAR szKeyName[ MAX_PATH + 20 ];
  125. wsprintf( szKeyName, _T( "%s\\shell\\open\\command" ), szFileType );
  126. lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, szKeyName, 0, KEY_READ, &hFileKey );
  127. if( ERROR_SUCCESS != lResult )
  128. {
  129. break;
  130. }
  131. dwLength = cbShellOpenCommand;
  132. lResult = RegQueryValueEx( hFileKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
  133. if( 0 == lResult )
  134. {
  135. fFoundExtensionCommand = TRUE;
  136. }
  137. }
  138. while( FALSE );
  139. //
  140. // If there was no application associated with .html files by extension, look for
  141. // an application associated with the http protocol
  142. //
  143. if( !fFoundExtensionCommand )
  144. {
  145. if( NULL != hKey )
  146. {
  147. RegCloseKey( hKey );
  148. }
  149. do
  150. {
  151. //
  152. // Find the command for the shell's open verb associated with the http protocol
  153. //
  154. lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( "http\\shell\\open\\command" ), 0, KEY_READ, &hKey );
  155. if( ERROR_SUCCESS != lResult )
  156. {
  157. break;
  158. }
  159. DWORD dwLength = cbShellOpenCommand;
  160. lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
  161. }
  162. while( FALSE );
  163. }
  164. if( NULL != hKey )
  165. {
  166. RegCloseKey( hKey );
  167. }
  168. if( NULL != hFileKey )
  169. {
  170. RegCloseKey( hFileKey );
  171. }
  172. return( HRESULT_FROM_WIN32( lResult ) );
  173. }
  174. ///////////////////////////////////////////////////////////////////////////////
  175. HRESULT LaunchURL( LPCWSTR pszURL )
  176. {
  177. HRESULT hr;
  178. //
  179. // Find the appropriate command to launch URLs with
  180. //
  181. TCHAR szShellOpenCommand[ MAX_PATH * 2 ];
  182. hr = GetShellOpenCommand( szShellOpenCommand, sizeof( szShellOpenCommand ) );
  183. if( FAILED( hr ) )
  184. {
  185. return( hr );
  186. }
  187. //
  188. // Build the appropriate command line, substituting our URL parameter
  189. //
  190. TCHAR szLaunchCommand[ 2000 ];
  191. LPTSTR pszParam = _tcsstr( szShellOpenCommand, _T( "\"%1\"" ) );
  192. if( NULL == pszParam )
  193. {
  194. pszParam = _tcsstr( szShellOpenCommand, _T( "\"%*\"" ) );
  195. }
  196. if( NULL != pszParam )
  197. {
  198. *pszParam = _T( '\0' ) ;
  199. wsprintf( szLaunchCommand, _T( "%s%ws%s" ), szShellOpenCommand, pszURL, pszParam + 4 );
  200. }
  201. else
  202. {
  203. wsprintf( szLaunchCommand, _T( "%s %ws" ), szShellOpenCommand, pszURL );
  204. }
  205. //
  206. // Find the application name, stripping quotes if necessary
  207. //
  208. TCHAR szExe[ MAX_PATH * 2 ];
  209. LPTSTR pchFirst = szShellOpenCommand;
  210. LPTSTR pchNext = NULL;
  211. while( _T( ' ' ) == *pchFirst )
  212. {
  213. pchFirst++;
  214. }
  215. if( _T( '"' ) == *pchFirst )
  216. {
  217. pchFirst++;
  218. pchNext = _tcschr( pchFirst, _T( '"' ) );
  219. }
  220. else
  221. {
  222. pchNext = _tcschr( pchFirst + 1, _T( ' ' ) );
  223. }
  224. if( NULL == pchNext )
  225. {
  226. pchNext = szShellOpenCommand + _tcslen( szShellOpenCommand );
  227. }
  228. _tcsncpy( szExe, pchFirst, pchNext - pchFirst );
  229. szExe[ pchNext - pchFirst ] = _T( '\0' ) ;
  230. //
  231. // Because of the extremely long length of the URLs, neither
  232. // WinExec, nor ShellExecute, were working correctly. For this reason
  233. // we use CreateProcess. The CreateProcess documentation in MSDN says
  234. // that the most robust way to call CreateProcess is to pass the full
  235. // command line, where the first element is the application name, in the
  236. // lpCommandLine parameter. In our case this is necesssary to get Netscape
  237. // to function properly.
  238. //
  239. PROCESS_INFORMATION ProcInfo;
  240. ZeroMemory( (LPVOID)&ProcInfo, sizeof( PROCESS_INFORMATION ) );
  241. STARTUPINFO StartUp;
  242. ZeroMemory( (LPVOID)&StartUp, sizeof( STARTUPINFO ) );
  243. StartUp.cb = sizeof(STARTUPINFO);
  244. if( !CreateProcess( szExe, szLaunchCommand, NULL, NULL,
  245. FALSE, 0, NULL, NULL, &StartUp, &ProcInfo) )
  246. {
  247. hr = HRESULT_FROM_WIN32( GetLastError() );
  248. }
  249. else
  250. {
  251. //
  252. // CreateProcess succeeded and we do not need the handles to the thread
  253. // or the process, so close them now.
  254. //
  255. if( NULL != ProcInfo.hThread )
  256. {
  257. CloseHandle( ProcInfo.hThread );
  258. }
  259. if( NULL != ProcInfo.hProcess )
  260. {
  261. CloseHandle( ProcInfo.hProcess );
  262. }
  263. }
  264. return( hr );
  265. }