1 /*
   2  * Copyright (c) 2011, 2017, 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 //#define _DEBUG
  35 
  36 #ifdef _DEBUG
  37 #include <iostream>
  38 #include <sstream>
  39 #endif
  40 
  41 using namespace std;
  42 
  43 #define MAX_KEY_LENGTH 255
  44 #define MAX_VALUE_NAME 16383
  45 
  46 bool from_string(int &result, string &str) {
  47     const char *p = str.c_str();
  48     int res = 0;
  49     for (int index = 0;; index++) {
  50         char c = str[index];
  51         if (c == 0 && index > 0) {
  52             result = res;
  53             return true;
  54         }
  55         if (c < '0' || c > '9')
  56             return false;
  57         res = res * 10 + (c - '0');
  58     }
  59 }
  60 
  61 void PrintCSBackupAPIErrorMessage(DWORD dwErr) {
  62 
  63     char wszMsgBuff[512]; // Buffer for text.
  64 
  65     DWORD dwChars; // Number of chars returned.
  66 
  67     // Try to get the message from the system errors.
  68     dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
  69             FORMAT_MESSAGE_IGNORE_INSERTS,
  70             NULL,
  71             dwErr,
  72             0,
  73             wszMsgBuff,
  74             512,
  75             NULL);
  76 
  77     if (0 == dwChars) {
  78         // The error code did not exist in the system errors.
  79         // Try ntdsbmsg.dll for the error code.
  80 
  81         HINSTANCE hInst;
  82 
  83         // Load the library.
  84         hInst = LoadLibraryA("ntdsbmsg.dll");
  85         if (NULL == hInst) {
  86 #ifdef _DEBUG
  87             cerr << "cannot load ntdsbmsg.dll\n";
  88 #endif
  89             return;
  90         }
  91 
  92         // Try getting message text from ntdsbmsg.
  93         dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE |
  94                 FORMAT_MESSAGE_IGNORE_INSERTS,
  95                 hInst,
  96                 dwErr,
  97                 0,
  98                 wszMsgBuff,
  99                 512,
 100                 NULL);
 101 
 102         // Free the library.
 103         FreeLibrary(hInst);
 104     }
 105 
 106     // Display the error message, or generic text if not found.
 107 #ifdef _DEBUG
 108     cerr << "Error value: " << dwErr << " Message: " << ((dwChars > 0) ? wszMsgBuff : "Error message not found.") << endl;
 109 #endif
 110 }
 111 
 112 class JavaVersion {
 113 public:
 114     int v1;
 115     int v2;
 116     int v3;
 117     std::wstring home;
 118     std::wstring path;
 119 
 120     JavaVersion(int pv1, int pv2, int pv3) {
 121         v1 = pv1;
 122         v2 = pv2;
 123         v3 = pv3;
 124     }
 125 
 126     bool operator>(const JavaVersion &other) const {
 127         if (v1 > other.v1)
 128             return true;
 129         if (v1 == other.v1) {
 130             if (v2 > other.v2)
 131                 return true;
 132             if (v2 == other.v2)
 133                 return v3 > other.v3;
 134         }
 135         return false;
 136     }
 137 
 138     bool operator>=(const JavaVersion &other) const {
 139         if (v1 > other.v1)
 140             return true;
 141         if (v1 == other.v1) {
 142             if (v2 > other.v2)
 143                 return true;
 144             if (v2 == other.v2)
 145                 return v3 >= other.v3;
 146         }
 147         return false;
 148     }
 149 
 150     bool operator<(const JavaVersion &other) const {
 151         if (v1 < other.v1)
 152             return true;
 153         if (v1 == other.v1) {
 154             if (v2 < other.v2)
 155                 return true;
 156             if (v2 == other.v2)
 157                 return v3 < other.v3;
 158         }
 159         return false;
 160     }
 161 };
 162 
 163 class EnvironmentVariable {
 164 private:
 165     std::wstring FValue;
 166 
 167 public:
 168     EnvironmentVariable(std::wstring Name) {
 169         wchar_t* value;
 170         size_t requiredSize;
 171 
 172         _wgetenv_s(&requiredSize, NULL, 0, Name.data());
 173 
 174         if (requiredSize != 0) {
 175             value = (wchar_t*)malloc(requiredSize * sizeof(wchar_t));
 176             if (value)
 177             {
 178                 // Get the value of the LIB environment variable.
 179                 _wgetenv_s(&requiredSize, value, requiredSize, Name.data());
 180                 FValue = value;
 181             }
 182         }
 183     }
 184 
 185     std::wstring get() {
 186         return FValue;
 187     }
 188 
 189     bool exists() {
 190         return !FValue.empty();
 191     }
 192 };
 193 
 194 bool checkJavaHome(HKEY key, const char * sKey, const char * jv, JavaVersion *version) {
 195     char p[MAX_KEY_LENGTH];
 196     HKEY hKey;
 197     bool result = false;
 198     int res;
 199 
 200     strcpy_s(p, MAX_KEY_LENGTH, sKey);
 201     strcat_s(p, MAX_KEY_LENGTH - strlen(p), "\\");
 202     strcat_s(p, MAX_KEY_LENGTH - strlen(p), jv);
 203 
 204     if (RegOpenKeyExA(key,
 205             p,
 206             0,
 207             KEY_READ,
 208             &hKey) == ERROR_SUCCESS
 209             ) {
 210         DWORD ot = REG_SZ;
 211         DWORD size = 255;
 212         wchar_t data[MAX_PATH] = { 0 };
 213         if ((res = RegQueryValueEx(hKey, L"JavaHome", NULL, &ot, (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     unsigned int nn = s.length();
 282     for (unsigned int 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 (unsigned int 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 
 426 int fileExists(const std::wstring& path) {
 427     WIN32_FIND_DATA ffd;
 428     HANDLE hFind;
 429 
 430     hFind = FindFirstFile(path.data(), &ffd);
 431     if (hFind == INVALID_HANDLE_VALUE)
 432         return FALSE;
 433 
 434     FindClose(hFind);
 435     return (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
 436 }
 437 
 438 bool hasEnding(std::wstring const &fullString, std::wstring const &ending) {
 439     if (fullString.length() >= ending.length()) {
 440         return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
 441     }
 442     else {
 443         return false;
 444     }
 445 }
 446 
 447 #define TRAILING_PATHSEPARATOR '\\'
 448 
 449 std::wstring ExtractFilePath(std::wstring Path) {
 450     std::wstring result;
 451     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 452     if (slash != std::wstring::npos)
 453         result = Path.substr(0, slash);
 454     return result;
 455 }
 456 
 457 std::wstring GetCurrentExecutableName() {
 458     TCHAR FileName[MAX_PATH];
 459     GetModuleFileName(NULL, FileName, MAX_PATH);
 460     return FileName;
 461 }
 462 
 463 int wmain(int argc, wchar_t* argv[]) {
 464     wchar_t buf[MAX_PATH];
 465     GetModuleFileName(NULL, buf, MAX_PATH);
 466     std::wstring javafxhome = buf;
 467 
 468     javafxhome.erase(javafxhome.rfind(L"\\"));
 469 
 470     std::wstring fxlib = javafxhome + L"\\..\\lib\\";
 471 
 472     EnvironmentVariable java_home(L"JAVA_HOME");
 473     std::wstring javacmd;
 474     std::wstring javahome;
 475 
 476     if (java_home.exists()) {
 477         javahome = java_home.get();
 478         javacmd = javahome + L"\\bin\\java.exe";
 479         std::wstring javaccmd = javahome + L"\\bin\\javac.exe";
 480         if (!fileExists(javacmd) || !fileExists(javaccmd)) {
 481             javacmd = L"";
 482             javahome = L"";
 483         }
 484     }
 485     else {
 486         std::wstring exe = GetCurrentExecutableName();
 487         javacmd = ExtractFilePath(exe) + L"\\java.exe";
 488     }
 489 
 490     if (javacmd.length() <= 0) {
 491         JavaVersion * jv2 = GetMaxVersion(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Development Kit");
 492         if (jv2 != NULL) {
 493             javacmd = jv2->path;
 494             javahome = jv2->home;
 495         }
 496         else {
 497             javacmd = L"java.exe";
 498         }
 499     }
 500 
 501     std::wstring cmd = L"\"" + javacmd + L"\"";
 502     if (javahome.length() > 0) {
 503         SetEnvironmentVariable(L"JAVA_HOME", javahome.c_str());
 504     }
 505 
 506     std::wstring memory = L"-Xmx512M";
 507     std::wstring debug = L"";
 508     std::wstring args = L"";
 509 
 510     for (int i = 1; i < argc; i++) {
 511         std::wstring argument = argv[i];
 512         std::wstring debug_arg = L"-J-Xdebug:";
 513         std::wstring icon_swap_arg = L"--icon-swap";
 514         std::wstring version_swap_arg = L"--version-swap";
 515 
 516         if (argument.find(L"-J-Xmx", 0) == 0) {
 517             memory = argument.substr(2, argument.length() - 2);
 518         }
 519         else if (argument.find(debug_arg, 0) == 0) {
 520             std::wstring address = argument.substr(debug_arg.length(), argument.length() - debug_arg.length());
 521             debug = L"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + address;
 522         }
 523         else if (argument.find(icon_swap_arg, 0) == 0) {
 524             if (argc != 4) {
 525                 fwprintf(stderr, TEXT("Usage: javapackager.exe --icon-swap [Icon File Name] [Executable File Name]\n"));
 526                 return 1;
 527             }
 528 
 529             wprintf(L"Icon File Name: %s\n", argv[i + 1]);
 530             wprintf(L"Executable File Name: %s\n", argv[i + 2]);
 531 
 532             if (ChangeIcon(argv[i + 1], argv[i + 2]) == true) {
 533                 return 0;
 534             }
 535             else {
 536                 fwprintf(stderr, TEXT("failed\n"));
 537                 return 1;
 538             }
 539         }
 540         else if (argument.find(version_swap_arg, 0) == 0) {
 541             if (argc != 4) {
 542                 fwprintf(stderr, TEXT("Usage: javapackager.exe --version-swap [Property File Name] [Executable File Name]\n"));
 543                 return 1;
 544             }
 545 
 546             fwprintf(stdout, TEXT("Resource File Name: %s\n"), argv[i + 1]);
 547             fwprintf(stdout, TEXT("Executable File Name: %s\n"), argv[i + 2]);
 548 
 549             VersionInfoSwap vs(argv[i + 1], argv[i + 2]);
 550 
 551             if (vs.PatchExecutable()) {
 552                 return 0;
 553             }
 554             else {
 555                 fwprintf(stderr, TEXT("failed\n"));
 556                 return 1;
 557             }
 558         }
 559         else {
 560             args = args + L" \"" + argv[i] + L"\"";
 561         }
 562     }
 563 
 564 
 565     cmd += debug +
 566         L" " + memory +
 567         L" -Djavafx.home=\"" + javafxhome +
 568         L"\" -classpath \"" + fxlib + L"\"ant-javafx.jar" +
 569         L" com.sun.javafx.tools.packager.Main" +
 570         L" " + args;
 571 
 572 #ifdef _DEBUG
 573     printf ("%s", cmd.c_str());
 574 #endif
 575 
 576     STARTUPINFO start;
 577     PROCESS_INFORMATION pi;
 578     memset(&start, 0, sizeof (start));
 579     start.cb = sizeof(start);
 580 
 581     if (!CreateProcess(NULL, (wchar_t *) cmd.data(),
 582             NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &start, &pi)) {
 583 #ifdef _DEBUG
 584         fprintf(stderr, "Cannot start java.exe");
 585 #endif
 586         return EXIT_FAILURE;
 587     }
 588 
 589     WaitForSingleObject(pi.hProcess, INFINITE);
 590     unsigned long exitCode;
 591     GetExitCodeProcess(pi.hProcess, &exitCode);
 592 
 593     CloseHandle(pi.hProcess);
 594     CloseHandle(pi.hThread);
 595 
 596     return exitCode;
 597 }