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