1 /* 2 * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string> 29 #include <windows.h> 30 31 #include "IconSwap.h" 32 #include "VersionInfoSwap.h" 33 34 #ifdef DEBUG 35 #include <iostream> 36 #include <sstream> 37 #endif 38 39 using namespace std; 40 41 #define MAX_KEY_LENGTH 255 42 #define MAX_VALUE_NAME 16383 43 #define TRAILING_PATHSEPARATOR '\\' 44 45 bool from_string(int &result, string &str) { 46 const char *p = str.c_str(); 47 int res = 0; 48 for (int index = 0;; index++) { 49 char c = str[index]; 50 if (c == 0 && index > 0) { 51 result = res; 52 return true; 53 } 54 if (c < '0' || c > '9') 55 return false; 56 res = res * 10 + (c - '0'); 57 } 58 } 59 60 void PrintCSBackupAPIErrorMessage(DWORD dwErr) { 61 62 char wszMsgBuff[512]; // Buffer for text. 63 64 DWORD dwChars; // Number of chars returned. 65 66 // Try to get the message from the system errors. 67 dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | 68 FORMAT_MESSAGE_IGNORE_INSERTS, 69 NULL, 70 dwErr, 71 0, 72 wszMsgBuff, 73 512, 74 NULL); 75 76 if (0 == dwChars) { 77 // The error code did not exist in the system errors. 78 // Try ntdsbmsg.dll for the error code. 79 HINSTANCE hInst; 80 81 // Load the library. 82 hInst = LoadLibraryA("ntdsbmsg.dll"); 83 if (NULL == hInst) { 84 #ifdef DEBUG 85 cerr << "cannot load ntdsbmsg.dll\n"; 86 #endif 87 return; 88 } 89 90 // Try getting message text from ntdsbmsg. 91 dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE | 92 FORMAT_MESSAGE_IGNORE_INSERTS, 93 hInst, 94 dwErr, 95 0, 96 wszMsgBuff, 97 512, 98 NULL); 99 100 // Free the library. 101 FreeLibrary(hInst); 102 } 103 104 // Display the error message, or generic text if not found. 105 #ifdef DEBUG 106 cerr << "Error value: " << dwErr << " Message: " << ((dwChars > 0) ? wszMsgBuff : "Error message not found.") << endl; 107 #endif 108 } 109 110 class JavaVersion { 111 public: 112 int v1; 113 int v2; 114 int v3; 115 std::wstring home; 116 std::wstring path; 117 118 JavaVersion(int pv1, int pv2, int pv3) { 119 v1 = pv1; 120 v2 = pv2; 121 v3 = pv3; 122 } 123 124 bool operator>(const JavaVersion &other) const { 125 if (v1 > other.v1) 126 return true; 127 if (v1 == other.v1) { 128 if (v2 > other.v2) 129 return true; 130 if (v2 == other.v2) 131 return v3 > other.v3; 132 } 133 return false; 134 } 135 136 bool operator>=(const JavaVersion &other) const { 137 if (v1 > other.v1) 138 return true; 139 if (v1 == other.v1) { 140 if (v2 > other.v2) 141 return true; 142 if (v2 == other.v2) 143 return v3 >= other.v3; 144 } 145 return false; 146 } 147 148 bool operator<(const JavaVersion &other) const { 149 if (v1 < other.v1) 150 return true; 151 if (v1 == other.v1) { 152 if (v2 < other.v2) 153 return true; 154 if (v2 == other.v2) 155 return v3 < other.v3; 156 } 157 return false; 158 } 159 }; 160 161 class EnvironmentVariable { 162 private: 163 std::wstring FValue; 164 165 public: 166 EnvironmentVariable(std::wstring Name) { 167 wchar_t* value; 168 size_t requiredSize; 169 170 _wgetenv_s(&requiredSize, NULL, 0, Name.data()); 171 172 if (requiredSize != 0) { 173 value = (wchar_t*)malloc(requiredSize * sizeof(wchar_t)); 174 if (value) 175 { 176 // Get the value of the LIB environment variable. 177 _wgetenv_s(&requiredSize, value, requiredSize, Name.data()); 178 FValue = value; 179 } 180 } 181 } 182 183 std::wstring get() { 184 return FValue; 185 } 186 187 bool exists() { 188 return !FValue.empty(); 189 } 190 }; 191 192 bool checkJavaHome(HKEY key, const char * sKey, const char * jv, 193 JavaVersion *version) { 194 char p[MAX_KEY_LENGTH]; 195 HKEY hKey; 196 bool result = false; 197 int res; 198 199 strcpy_s(p, MAX_KEY_LENGTH, sKey); 200 strcat_s(p, MAX_KEY_LENGTH - strlen(p), "\\"); 201 strcat_s(p, MAX_KEY_LENGTH - strlen(p), jv); 202 203 if (RegOpenKeyExA(key, 204 p, 205 0, 206 KEY_READ, 207 &hKey) == ERROR_SUCCESS 208 ) { 209 DWORD ot = REG_SZ; 210 DWORD size = 255; 211 wchar_t data[MAX_PATH] = { 0 }; 212 if ((res = RegQueryValueEx(hKey, L"JavaHome", NULL, &ot, 213 (BYTE *)data, &size)) == ERROR_SUCCESS) { 214 version->home = data; 215 std::wstring ldata = std::wstring(data) + L"\\bin\\java.exe"; 216 version->path = data; 217 result = GetFileAttributes(data) != 0xFFFFFFFF; 218 } 219 else { 220 PrintCSBackupAPIErrorMessage(res); 221 result = false; 222 } 223 RegCloseKey(hKey); 224 } 225 else { 226 #ifdef DEBUG 227 cerr << "Can not open registry key" << endl; 228 #endif 229 result = false; 230 } 231 232 return result; 233 } 234 235 JavaVersion * parseName(const char * jName) { 236 string s(jName); 237 238 if (s.length() == 0) { 239 return NULL; 240 } 241 242 string n; 243 string::size_type pos; 244 245 pos = s.find_first_of("."); 246 if (pos != string::npos) { 247 n = s.substr(0, pos); 248 s = s.substr(pos + 1); 249 } 250 else { 251 n = s; 252 s = ""; 253 } 254 255 int v1 = 0; 256 257 if (n.length() > 0) { 258 if (!from_string(v1, n)) 259 return NULL; 260 } 261 262 263 pos = s.find_first_of("."); 264 if (pos != string::npos) { 265 n = s.substr(0, pos); 266 s = s.substr(pos + 1); 267 } 268 else { 269 n = s; 270 s = ""; 271 } 272 273 int v2 = 0; 274 275 if (n.length() > 0) { 276 if (!from_string(v2, n)) 277 return NULL; 278 } 279 280 281 size_t nn = s.length(); 282 for (size_t i = 0; i < s.length(); i++) { 283 string c = s.substr(i, 1); 284 int tmp; 285 if (!from_string(tmp, c)) { 286 nn = i; 287 break; 288 } 289 } 290 291 n = s.substr(0, nn); 292 if (nn < s.length()) { 293 s = s.substr(nn + 1); 294 } 295 else s = ""; 296 297 int v3 = 0; 298 299 if (n.length() > 0) { 300 if (!from_string(v3, n)) 301 v3 = 0; 302 } 303 304 int v4 = 0; 305 306 // update version 307 if (s.length() > 0) { 308 nn = s.length(); 309 for (size_t i = 0; i < s.length(); i++) { 310 string c = s.substr(i, 1); 311 int tmp; 312 if (!from_string(tmp, c)) { 313 nn = i; 314 break; 315 } 316 } 317 318 n = s.substr(0, nn); 319 320 if (n.length() > 0) { 321 if (!from_string(v4, n)) 322 v4 = 0; 323 } 324 } 325 326 return new JavaVersion(v2, v3, v4); 327 } 328 329 JavaVersion * GetMaxVersion(HKEY key, const char * sKey) { 330 HKEY hKey; 331 JavaVersion * result = NULL; 332 333 if (RegOpenKeyExA(key, 334 sKey, 335 0, 336 KEY_READ, 337 &hKey) == ERROR_SUCCESS 338 ) { 339 DWORD retCode; 340 char achClass[MAX_PATH]; // buffer for class name 341 DWORD cchClassName = MAX_PATH; // size of class string 342 343 344 DWORD cchValue = MAX_VALUE_NAME; 345 DWORD cSubKeys = 0; // number of subkeys 346 DWORD cbMaxSubKey; // longest subkey size 347 DWORD cchMaxClass; // longest class string 348 DWORD cValues; // number of values for key 349 DWORD cchMaxValue; // longest value name 350 DWORD cbMaxValueData; // longest value data 351 DWORD cbSecurityDescriptor; // size of security descriptor 352 FILETIME ftLastWriteTime; // last write time 353 354 retCode = RegQueryInfoKeyA( 355 hKey, // key handle 356 achClass, // buffer for class name 357 &cchClassName, // size of class string 358 NULL, // reserved 359 &cSubKeys, // number of subkeys 360 &cbMaxSubKey, // longest subkey size 361 &cchMaxClass, // longest class string 362 &cValues, // number of values for this key 363 &cchMaxValue, // longest value name 364 &cbMaxValueData, // longest value data 365 &cbSecurityDescriptor, // security descriptor 366 &ftLastWriteTime); // last write time 367 368 if (cSubKeys) { 369 for (unsigned int i = 0; i < cSubKeys; i++) { 370 char achKey[MAX_KEY_LENGTH]; // buffer for subkey name 371 DWORD cbName = MAX_KEY_LENGTH; 372 retCode = RegEnumKeyExA(hKey, i, 373 achKey, 374 &cbName, 375 NULL, 376 NULL, 377 NULL, 378 &ftLastWriteTime); 379 380 if (retCode == ERROR_SUCCESS) { 381 #ifdef DEBUG 382 cout << achKey << endl; 383 #endif 384 JavaVersion * nv = parseName(achKey); 385 386 bool isHome = checkJavaHome(key, sKey, achKey, nv); 387 #ifdef DEBUG 388 wcout << nv->home << " " << isHome << endl; 389 #endif 390 391 if (isHome) 392 if (result == NULL) { 393 result = nv; 394 #ifdef DEBUG 395 cout << "NEW" << endl; 396 #endif 397 } 398 else { 399 if (nv != NULL) { 400 if (*nv > *result) { 401 #ifdef DEBUG 402 cout << "REPLACE" << endl; 403 #endif 404 delete result; 405 result = nv; 406 } 407 else { 408 #ifdef DEBUG 409 cout << "NO" << endl; 410 #endif 411 delete nv; 412 } 413 } 414 } 415 } 416 } 417 } 418 419 RegCloseKey(hKey); 420 } 421 422 return result; 423 } 424 425 int fileExists(const std::wstring& path) { 426 WIN32_FIND_DATA ffd; 427 HANDLE hFind; 428 429 hFind = FindFirstFile(path.data(), &ffd); 430 if (hFind == INVALID_HANDLE_VALUE) 431 return FALSE; 432 433 FindClose(hFind); 434 return (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; 435 } 436 437 bool hasEnding(std::wstring const &fullString, std::wstring const &ending) { 438 if (fullString.length() >= ending.length()) { 439 return (0 == fullString.compare(fullString.length() - ending.length(), 440 ending.length(), ending)); 441 } 442 else { 443 return false; 444 } 445 } 446 447 std::wstring ExtractFilePath(std::wstring Path) { 448 std::wstring result; 449 size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); 450 if (slash != std::wstring::npos) 451 result = Path.substr(0, slash); 452 return result; 453 } 454 455 std::wstring GetCurrentExecutableName() { 456 TCHAR FileName[MAX_PATH]; 457 GetModuleFileName(NULL, FileName, MAX_PATH); 458 return FileName; 459 } 460 461 int wmain(int argc, wchar_t* argv[]) { 462 wchar_t buf[MAX_PATH]; 463 GetModuleFileName(NULL, buf, MAX_PATH); 464 465 std::wstring javacmd; 466 std::wstring javahome; 467 468 std::wstring exe = GetCurrentExecutableName(); 469 470 if (exe.length() <= 0) { 471 JavaVersion * jv2 = GetMaxVersion(HKEY_LOCAL_MACHINE, 472 "SOFTWARE\\JavaSoft\\JDK"); 473 if (jv2 != NULL) { 474 javahome = jv2->home; 475 javacmd = javahome + L"\\bin\\" + L"\\java.exe"; 476 } 477 else { 478 javacmd = L"java.exe"; 479 } 480 } else { 481 javacmd = ExtractFilePath(exe) + L"\\java.exe"; 482 } 483 484 std::wstring cmd = L"\"" + javacmd + L"\""; 485 if (javahome.length() > 0) { 486 SetEnvironmentVariable(L"JAVA_HOME", javahome.c_str()); 487 } 488 489 std::wstring memory = L"-Xmx512M"; 490 std::wstring debug = L""; 491 std::wstring args = L""; 492 493 for (int i = 1; i < argc; i++) { 494 std::wstring argument = argv[i]; 495 std::wstring debug_arg = L"-J-Xdebug:"; 496 std::wstring icon_swap_arg = L"--icon-swap"; 497 std::wstring version_swap_arg = L"--version-swap"; 498 499 if (argument.find(L"-J-Xmx", 0) == 0) { 500 memory = argument.substr(2, argument.length() - 2); 501 } 502 else if (argument.find(debug_arg, 0) == 0) { 503 std::wstring address = argument.substr(debug_arg.length(), 504 argument.length() - debug_arg.length()); 505 debug = L"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + address; 506 } 507 else if (argument.find(icon_swap_arg, 0) == 0) { 508 if (argc != 4) { 509 fwprintf(stderr, TEXT("Usage: jpackage.exe --icon-swap [Icon File Name] [Executable File Name]\n")); 510 return 1; 511 } 512 513 wprintf(L"Icon File Name: %s\n", argv[i + 1]); 514 wprintf(L"Executable File Name: %s\n", argv[i + 2]); 515 516 if (ChangeIcon(argv[i + 1], argv[i + 2]) == true) { 517 return 0; 518 } 519 else { 520 fwprintf(stderr, TEXT("failed\n")); 521 return 1; 522 } 523 } 524 else if (argument.find(version_swap_arg, 0) == 0) { 525 if (argc != 4) { 526 fwprintf(stderr, TEXT("Usage: jpackage.exe --version-swap [Property File Name] [Executable File Name]\n")); 527 return 1; 528 } 529 530 fwprintf(stdout, TEXT("Resource File Name: %s\n"), argv[i + 1]); 531 fwprintf(stdout, TEXT("Executable File Name: %s\n"), argv[i + 2]); 532 533 VersionInfoSwap vs(argv[i + 1], argv[i + 2]); 534 535 if (vs.PatchExecutable()) { 536 return 0; 537 } 538 else { 539 fwprintf(stderr, TEXT("failed\n")); 540 return 1; 541 } 542 } 543 else { 544 args = args + L" \"" + argv[i] + L"\""; 545 } 546 } 547 548 549 cmd += debug + L" " + memory + 550 L" -m jdk.jpackage/jdk.jpackage.main.Main" + 551 L" " + args; 552 553 #ifdef DEBUG 554 fwprintf (stdout, TEXT("%s\n"), cmd.c_str()); 555 #endif 556 557 STARTUPINFO start; 558 PROCESS_INFORMATION pi; 559 memset(&start, 0, sizeof (start)); 560 start.cb = sizeof(start); 561 562 if (!CreateProcess(NULL, (wchar_t *) cmd.data(), 563 NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &start, &pi)) { 564 #ifdef DEBUG 565 fprintf(stderr, "Cannot start java.exe"); 566 #endif 567 return EXIT_FAILURE; 568 } 569 570 WaitForSingleObject(pi.hProcess, INFINITE); 571 unsigned long exitCode; 572 GetExitCodeProcess(pi.hProcess, &exitCode); 573 574 CloseHandle(pi.hProcess); 575 CloseHandle(pi.hThread); 576 577 return exitCode; 578 }