1 /*
   2  * Copyright (c) 2011, 2018, 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: jpackager.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: jpackager.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.jpackager/jdk.jpackager.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 }