1 /*
   2  * Copyright (c) 2014, 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 "Platform.h"
  27 
  28 #include "JavaVirtualMachine.h"
  29 #include "LinuxPlatform.h"
  30 #include "PlatformString.h"
  31 #include "IniFile.h"
  32 #include "Helpers.h"
  33 #include "FilePath.h"
  34 
  35 #include <stdlib.h>
  36 #include <pwd.h>
  37 #include <sys/file.h>
  38 #include <sys/stat.h>
  39 #include <errno.h>
  40 #include <unistd.h>
  41 #include <sys/types.h>
  42 #include <limits.h>
  43 #include <signal.h>
  44 
  45 #define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp"
  46 
  47 TString GetEnv(const TString &name) {
  48     TString result;
  49 
  50     char *value = ::getenv((TCHAR*) name.c_str());
  51 
  52     if (value != NULL) {
  53         result = value;
  54     }
  55 
  56     return result;
  57 }
  58 
  59 LinuxPlatform::LinuxPlatform(void) : Platform(),
  60 PosixPlatform() {
  61     FMainThread = pthread_self();
  62 }
  63 
  64 LinuxPlatform::~LinuxPlatform(void) {
  65 }
  66 
  67 TString LinuxPlatform::GetPackageAppDirectory() {
  68     return FilePath::IncludeTrailingSeparator(
  69             GetPackageRootDirectory()) + _T("app");
  70 }
  71 
  72 TString LinuxPlatform::GetAppName() {
  73     TString result = GetModuleFileName();
  74     result = FilePath::ExtractFileName(result);
  75     return result;
  76 }
  77 
  78 TString LinuxPlatform::GetPackageLauncherDirectory() {
  79     return FilePath::IncludeTrailingSeparator(
  80             GetPackageRootDirectory()) + _T("bin");
  81 }
  82 
  83 TString LinuxPlatform::GetPackageRuntimeBinDirectory() {
  84     return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime/bin");
  85 }
  86 
  87 void LinuxPlatform::ShowMessage(TString title, TString description) {
  88     printf("%s %s\n", PlatformString(title).toPlatformString(),
  89             PlatformString(description).toPlatformString());
  90     fflush(stdout);
  91 }
  92 
  93 void LinuxPlatform::ShowMessage(TString description) {
  94     TString appname = GetModuleFileName();
  95     appname = FilePath::ExtractFileName(appname);
  96     ShowMessage(PlatformString(appname).toPlatformString(),
  97             PlatformString(description).toPlatformString());
  98 }
  99 
 100 TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source,
 101         bool &release) {
 102     // Not Implemented.
 103     return NULL;
 104 }
 105 
 106 TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source,
 107         bool &release) {
 108     // Not Implemented.
 109     return NULL;
 110 }
 111 
 112 TString LinuxPlatform::GetModuleFileName() {
 113     ssize_t len = 0;
 114     TString result;
 115     DynamicBuffer<TCHAR> buffer(MAX_PATH);
 116     if (buffer.GetData() == NULL) {
 117         return result;
 118     }
 119 
 120     if ((len = readlink("/proc/self/exe", buffer.GetData(),
 121             MAX_PATH - 1)) != -1) {
 122         buffer[len] = '\0';
 123         result = buffer.GetData();
 124     }
 125 
 126     return result;
 127 }
 128 
 129 void LinuxPlatform::SetCurrentDirectory(TString Value) {
 130     chdir(PlatformString(Value).toPlatformString());
 131 }
 132 
 133 TString LinuxPlatform::GetPackageRootDirectory() {
 134     TString result;
 135     TString filename = GetModuleFileName();
 136     TString binPath = FilePath::ExtractFilePath(filename);
 137 
 138     size_t slash = binPath.find_last_of(TRAILING_PATHSEPARATOR);
 139     if (slash != TString::npos) {
 140         result = binPath.substr(0, slash);
 141     }
 142 
 143     return result;
 144 }
 145 
 146 TString LinuxPlatform::GetAppDataDirectory() {
 147     TString result;
 148     TString home = GetEnv(_T("HOME"));
 149 
 150     if (home.empty() == false) {
 151         result += FilePath::IncludeTrailingSeparator(home) + _T(".local");
 152     }
 153 
 154     return result;
 155 }
 156 
 157 ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) {
 158     IniFile *result = new IniFile();
 159     if (result == NULL) {
 160         return NULL;
 161     }
 162 
 163     result->LoadFromFile(FileName);
 164 
 165     return result;
 166 }
 167 
 168 TString LinuxPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) {
 169     TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 170             "lib/libjli.so";
 171 
 172     if (FilePath::FileExists(result) == false) {
 173         result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 174                 "lib/jli/libjli.so";
 175         if (FilePath::FileExists(result) == false) {
 176             printf("Cannot find libjli.so!");
 177         }
 178     }
 179 
 180     return result;
 181 }
 182 
 183 bool LinuxPlatform::IsMainThread() {
 184     bool result = (FMainThread == pthread_self());
 185     return result;
 186 }
 187 
 188 TString LinuxPlatform::getTmpDirString() {
 189     return TString(LINUX_JPACKAGE_TMP_DIR);
 190 }
 191 
 192 TPlatformNumber LinuxPlatform::GetMemorySize() {
 193     long pages = sysconf(_SC_PHYS_PAGES);
 194     long page_size = sysconf(_SC_PAGE_SIZE);
 195     TPlatformNumber result = pages * page_size;
 196     result = result / 1048576; // Convert from bytes to megabytes.
 197     return result;
 198 }
 199 
 200 void PosixProcess::Cleanup() {
 201     if (FOutputHandle != 0) {
 202         close(FOutputHandle);
 203         FOutputHandle = 0;
 204     }
 205 
 206     if (FInputHandle != 0) {
 207         close(FInputHandle);
 208         FInputHandle = 0;
 209     }
 210 }
 211 
 212 #define PIPE_READ 0
 213 #define PIPE_WRITE 1
 214 
 215 bool PosixProcess::Execute(const TString Application,
 216         const std::vector<TString> Arguments, bool AWait) {
 217     bool result = false;
 218 
 219     if (FRunning == false) {
 220         FRunning = true;
 221 
 222         int handles[2];
 223 
 224         if (pipe(handles) == -1) {
 225             return false;
 226         }
 227 
 228         struct sigaction sa;
 229         sa.sa_handler = SIG_IGN;
 230         sigemptyset(&sa.sa_mask);
 231         sa.sa_flags = 0;
 232 
 233         FChildPID = fork();
 234 
 235         // PID returned by vfork is 0 for the child process and the
 236         // PID of the child process for the parent.
 237         if (FChildPID == -1) {
 238             // Error
 239             TString message = PlatformString::Format(
 240                     _T("Error: Unable to create process %s"),
 241                     Application.data());
 242             throw Exception(message);
 243         } else if (FChildPID == 0) {
 244             Cleanup();
 245             TString command = Application;
 246 
 247             for (std::vector<TString>::const_iterator iterator =
 248                     Arguments.begin(); iterator != Arguments.end();
 249                     iterator++) {
 250                 command += TString(_T(" ")) + *iterator;
 251             }
 252 #ifdef DEBUG
 253             printf("%s\n", command.data());
 254 #endif // DEBUG
 255 
 256             dup2(handles[PIPE_READ], STDIN_FILENO);
 257             dup2(handles[PIPE_WRITE], STDOUT_FILENO);
 258 
 259             close(handles[PIPE_READ]);
 260             close(handles[PIPE_WRITE]);
 261 
 262             execl("/bin/sh", "sh", "-c", command.data(), (char *) 0);
 263 
 264             _exit(127);
 265         } else {
 266             FOutputHandle = handles[PIPE_READ];
 267             FInputHandle = handles[PIPE_WRITE];
 268 
 269             if (AWait == true) {
 270                 ReadOutput();
 271                 Wait();
 272                 Cleanup();
 273                 FRunning = false;
 274                 result = true;
 275             } else {
 276                 result = true;
 277             }
 278         }
 279     }
 280 
 281     return result;
 282 }
 283 
 284 
 285 //----------------------------------------------------------------------------
 286 
 287 #ifndef __UNIX_JPACKAGE_PLATFORM__
 288 #define __UNIX_JPACKAGE_PLATFORM__
 289 
 290 /** Provide an abstraction for difference in the platform APIs,
 291      e.g. string manipulation functions, etc. */
 292 #include <stdio.h>
 293 #include <string.h>
 294 #include <strings.h>
 295 #include <sys/stat.h>
 296 
 297 #define TCHAR char
 298 
 299 #define _T(x) x
 300 
 301 #define JPACKAGE_MULTIBYTE_SNPRINTF snprintf
 302 
 303 #define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \
 304     snprintf((buffer), (count), (format), __VA_ARGS__)
 305 
 306 #define JPACKAGE_PRINTF(format, ...) \
 307     printf((format), ##__VA_ARGS__)
 308 
 309 #define JPACKAGE_FPRINTF(dest, format, ...) \
 310     fprintf((dest), (format), __VA_ARGS__)
 311 
 312 #define JPACKAGE_SSCANF(buf, format, ...) \
 313     sscanf((buf), (format), __VA_ARGS__)
 314 
 315 #define JPACKAGE_STRDUP(strSource) \
 316     strdup((strSource))
 317 
 318 //return "error code" (like on Windows)
 319 
 320 static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements,
 321         const char *strSource, size_t count) {
 322     char *s = strncpy(strDest, strSource, count);
 323     // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL
 324     // terminator at the end of the string.
 325     if (count < numberOfElements) {
 326         s[count] = '\0';
 327     } else {
 328         s[numberOfElements - 1] = '\0';
 329     }
 330     return (s == strDest) ? 0 : 1;
 331 }
 332 
 333 #define JPACKAGE_STRICMP(x, y) \
 334     strcasecmp((x), (y))
 335 
 336 #define JPACKAGE_STRNICMP(x, y, cnt) \
 337     strncasecmp((x), (y), (cnt))
 338 
 339 #define JPACKAGE_STRNCMP(x, y, cnt) \
 340     strncmp((x), (y), (cnt))
 341 
 342 #define JPACKAGE_STRLEN(x) \
 343     strlen((x))
 344 
 345 #define JPACKAGE_STRSTR(x, y) \
 346     strstr((x), (y))
 347 
 348 #define JPACKAGE_STRCHR(x, y) \
 349     strchr((x), (y))
 350 
 351 #define JPACKAGE_STRRCHR(x, y) \
 352     strrchr((x), (y))
 353 
 354 #define JPACKAGE_STRPBRK(x, y) \
 355     strpbrk((x), (y))
 356 
 357 #define JPACKAGE_GETENV(x) \
 358     getenv((x))
 359 
 360 #define JPACKAGE_PUTENV(x) \
 361     putenv((x))
 362 
 363 #define JPACKAGE_STRCMP(x, y) \
 364     strcmp((x), (y))
 365 
 366 #define JPACKAGE_STRCPY(x, y) \
 367     strcpy((x), (y))
 368 
 369 #define JPACKAGE_STRCAT(x, y) \
 370     strcat((x), (y))
 371 
 372 #define JPACKAGE_ATOI(x) \
 373     atoi((x))
 374 
 375 #define JPACKAGE_FOPEN(x, y) \
 376     fopen((x), (y))
 377 
 378 #define JPACKAGE_FGETS(x, y, z) \
 379     fgets((x), (y), (z))
 380 
 381 #define JPACKAGE_REMOVE(x) \
 382     remove((x))
 383 
 384 #define JPACKAGE_SPAWNV(mode, cmd, args) \
 385     spawnv((mode), (cmd), (args))
 386 
 387 #define JPACKAGE_ISDIGIT(ch) isdigit(ch)
 388 
 389 // for non-unicode, just return the input string for
 390 // the following 2 conversions
 391 #define JPACKAGE_NEW_MULTIBYTE(message) message
 392 
 393 #define JPACKAGE_NEW_FROM_MULTIBYTE(message) message
 394 
 395 // for non-unicode, no-op for the relase operation
 396 // since there is no memory allocated for the
 397 // string conversions
 398 #define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS)
 399 
 400 #define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS)
 401 
 402 // The size will be used for converting from 1 byte to 1 byte encoding.
 403 // Ensure have space for zero-terminator.
 404 #define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1)
 405 
 406 #endif
 407 #define xmlTagType    0
 408 #define xmlPCDataType 1
 409 
 410 typedef struct _xmlNode XMLNode;
 411 typedef struct _xmlAttribute XMLAttribute;
 412 
 413 struct _xmlNode {
 414     int _type; // Type of node: tag, pcdata, cdate
 415     TCHAR* _name; // Contents of node
 416     XMLNode* _next; // Next node at same level
 417     XMLNode* _sub; // First sub-node
 418     XMLAttribute* _attributes; // List of attributes
 419 };
 420 
 421 struct _xmlAttribute {
 422     TCHAR* _name; // Name of attribute
 423     TCHAR* _value; // Value of attribute
 424     XMLAttribute* _next; // Next attribute for this tag
 425 };
 426 
 427 // Public interface
 428 static void RemoveNonAsciiUTF8FromBuffer(char *buf);
 429 XMLNode* ParseXMLDocument(TCHAR* buf);
 430 void FreeXMLDocument(XMLNode* root);
 431 
 432 // Utility methods for parsing document
 433 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name);
 434 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name);
 435 
 436 // Debugging
 437 void PrintXMLDocument(XMLNode* node, int indt);
 438 
 439 #include <sys/types.h>
 440 #include <sys/stat.h>
 441 #include <setjmp.h>
 442 #include <stdlib.h>
 443 #include <wctype.h>
 444 
 445 #define JWS_assert(s, msg)      \
 446     if (!(s)) { Abort(msg); }
 447 
 448 
 449 // Internal declarations
 450 static XMLNode* ParseXMLElement(void);
 451 static XMLAttribute* ParseXMLAttribute(void);
 452 static TCHAR* SkipWhiteSpace(TCHAR *p);
 453 static TCHAR* SkipXMLName(TCHAR *p);
 454 static TCHAR* SkipXMLComment(TCHAR *p);
 455 static TCHAR* SkipXMLDocType(TCHAR *p);
 456 static TCHAR* SkipXMLProlog(TCHAR *p);
 457 static TCHAR* SkipPCData(TCHAR *p);
 458 static int IsPCData(TCHAR *p);
 459 static void ConvertBuiltInEntities(TCHAR* p);
 460 static void SetToken(int type, TCHAR* start, TCHAR* end);
 461 static void GetNextToken(void);
 462 static XMLNode* CreateXMLNode(int type, TCHAR* name);
 463 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value);
 464 static XMLNode* ParseXMLElement(void);
 465 static XMLAttribute* ParseXMLAttribute(void);
 466 static void FreeXMLAttribute(XMLAttribute* attr);
 467 static void PrintXMLAttributes(XMLAttribute* attr);
 468 static void indent(int indt);
 469 
 470 static jmp_buf jmpbuf;
 471 static XMLNode* root_node = NULL;
 472 
 473 /** definition of error codes for setjmp/longjmp,
 474  *  that can be handled in ParseXMLDocument()
 475  */
 476 #define JMP_NO_ERROR     0
 477 #define JMP_OUT_OF_RANGE 1
 478 
 479 #define NEXT_CHAR(p) { \
 480     if (*p != 0) { \
 481         p++; \
 482     } else { \
 483         longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
 484     } \
 485 }
 486 #define NEXT_CHAR_OR_BREAK(p) { \
 487     if (*p != 0) { \
 488         p++; \
 489     } else { \
 490         break; \
 491     } \
 492 }
 493 #define NEXT_CHAR_OR_RETURN(p) { \
 494     if (*p != 0) { \
 495         p++; \
 496     } else { \
 497         return; \
 498     } \
 499 }
 500 #define SKIP_CHARS(p,n) { \
 501     int i; \
 502     for (i = 0; i < (n); i++) { \
 503         if (*p != 0) { \
 504             p++; \
 505         } else { \
 506            longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
 507         } \
 508     } \
 509 }
 510 #define SKIP_CHARS_OR_BREAK(p,n) { \
 511     int i; \
 512     for (i = 0; i < (n); i++) { \
 513         if (*p != 0) { \
 514             p++; \
 515         } else { \
 516             break; \
 517         } \
 518     } \
 519     if (i < (n)) { \
 520         break; \
 521     } \
 522 }
 523 
 524 /** Iterates through the null-terminated buffer (i.e., C string) and
 525  *  replaces all UTF-8 encoded character >255 with 255
 526  *
 527  *  UTF-8 encoding:
 528  *
 529  *   Range A:  0x0000 - 0x007F
 530  *                               0 | bits 0 - 7
 531  *   Range B : 0x0080 - 0x07FF  :
 532  *                               110 | bits 6 - 10
 533  *                               10  | bits 0 - 5
 534  *   Range C : 0x0800 - 0xFFFF  :
 535  *                               1110 | bits 12-15
 536  *                               10   | bits  6-11
 537  *                               10   | bits  0-5
 538  */
 539 static void RemoveNonAsciiUTF8FromBuffer(char *buf) {
 540     char* p;
 541     char* q;
 542     char c;
 543     p = q = buf;
 544     // We are not using NEXT_CHAR() to check if *q is NULL, as q is output
 545     // location and offset for q is smaller than for p.
 546     while (*p != '\0') {
 547         c = *p;
 548         if ((c & 0x80) == 0) {
 549             /* Range A */
 550             *q++ = *p;
 551             NEXT_CHAR(p);
 552         } else if ((c & 0xE0) == 0xC0) {
 553             /* Range B */
 554             *q++ = (char) 0xFF;
 555             NEXT_CHAR(p);
 556             NEXT_CHAR_OR_BREAK(p);
 557         } else {
 558             /* Range C */
 559             *q++ = (char) 0xFF;
 560             NEXT_CHAR(p);
 561             SKIP_CHARS_OR_BREAK(p, 2);
 562         }
 563     }
 564     /* Null terminate string */
 565     *q = '\0';
 566 }
 567 
 568 static TCHAR* SkipWhiteSpace(TCHAR *p) {
 569     if (p != NULL) {
 570         while (iswspace(*p))
 571             NEXT_CHAR_OR_BREAK(p);
 572     }
 573     return p;
 574 }
 575 
 576 static TCHAR* SkipXMLName(TCHAR *p) {
 577     TCHAR c = *p;
 578     /* Check if start of token */
 579     if (('a' <= c && c <= 'z') ||
 580             ('A' <= c && c <= 'Z') ||
 581             c == '_' || c == ':') {
 582 
 583         while (('a' <= c && c <= 'z') ||
 584                 ('A' <= c && c <= 'Z') ||
 585                 ('0' <= c && c <= '9') ||
 586                 c == '_' || c == ':' || c == '.' || c == '-') {
 587             NEXT_CHAR(p);
 588             c = *p;
 589             if (c == '\0') break;
 590         }
 591     }
 592     return p;
 593 }
 594 
 595 static TCHAR* SkipXMLComment(TCHAR *p) {
 596     if (p != NULL) {
 597         if (JPACKAGE_STRNCMP(p, _T("<!--"), 4) == 0) {
 598             SKIP_CHARS(p, 4);
 599             do {
 600                 if (JPACKAGE_STRNCMP(p, _T("-->"), 3) == 0) {
 601                     SKIP_CHARS(p, 3);
 602                     return p;
 603                 }
 604                 NEXT_CHAR(p);
 605             } while (*p != '\0');
 606         }
 607     }
 608     return p;
 609 }
 610 
 611 static TCHAR* SkipXMLDocType(TCHAR *p) {
 612     if (p != NULL) {
 613         if (JPACKAGE_STRNCMP(p, _T("<!"), 2) == 0) {
 614             SKIP_CHARS(p, 2);
 615             while (*p != '\0') {
 616                 if (*p == '>') {
 617                     NEXT_CHAR(p);
 618                     return p;
 619                 }
 620                 NEXT_CHAR(p);
 621             }
 622         }
 623     }
 624     return p;
 625 }
 626 
 627 static TCHAR* SkipXMLProlog(TCHAR *p) {
 628     if (p != NULL) {
 629         if (JPACKAGE_STRNCMP(p, _T("<?"), 2) == 0) {
 630             SKIP_CHARS(p, 2);
 631             do {
 632                 if (JPACKAGE_STRNCMP(p, _T("?>"), 2) == 0) {
 633                     SKIP_CHARS(p, 2);
 634                     return p;
 635                 }
 636                 NEXT_CHAR(p);
 637             } while (*p != '\0');
 638         }
 639     }
 640     return p;
 641 }
 642 
 643 /* Search for the built-in XML entities:
 644  * &amp; (&), &lt; (<), &gt; (>), &apos; ('), and &quote(")
 645  * and convert them to a real TCHARacter
 646  */
 647 static void ConvertBuiltInEntities(TCHAR* p) {
 648     TCHAR* q;
 649     q = p;
 650     // We are not using NEXT_CHAR() to check if *q is NULL,
 651     // as q is output location and offset for q is smaller than for p.
 652     while (*p) {
 653         if (IsPCData(p)) {
 654             /* dont convert &xxx values within PData */
 655             TCHAR *end;
 656             end = SkipPCData(p);
 657             while (p < end) {
 658                 *q++ = *p;
 659                 NEXT_CHAR(p);
 660             }
 661         } else {
 662             if (JPACKAGE_STRNCMP(p, _T("&amp;"), 5) == 0) {
 663                 *q++ = '&';
 664                 SKIP_CHARS(p, 5);
 665             } else if (JPACKAGE_STRNCMP(p, _T("&lt;"), 4) == 0) {
 666                 *q = '<';
 667                 SKIP_CHARS(p, 4);
 668             } else if (JPACKAGE_STRNCMP(p, _T("&gt;"), 4) == 0) {
 669                 *q = '>';
 670                 SKIP_CHARS(p, 4);
 671             } else if (JPACKAGE_STRNCMP(p, _T("&apos;"), 6) == 0) {
 672                 *q = '\'';
 673                 SKIP_CHARS(p, 6);
 674             } else if (JPACKAGE_STRNCMP(p, _T("&quote;"), 7) == 0) {
 675                 *q = '\"';
 676                 SKIP_CHARS(p, 7);
 677             } else {
 678                 *q++ = *p;
 679                 NEXT_CHAR(p);
 680             }
 681         }
 682     }
 683     *q = '\0';
 684 }
 685 
 686 /* ------------------------------------------------------------- */
 687 /* XML tokenizer */
 688 
 689 #define TOKEN_UNKNOWN             0
 690 #define TOKEN_BEGIN_TAG           1  /* <tag */
 691 #define TOKEN_END_TAG             2  /* </tag */
 692 #define TOKEN_CLOSE_BRACKET       3  /* >  */
 693 #define TOKEN_EMPTY_CLOSE_BRACKET 4  /* /> */
 694 #define TOKEN_PCDATA              5  /* pcdata */
 695 #define TOKEN_CDATA               6  /* cdata */
 696 #define TOKEN_EOF                 7
 697 
 698 static TCHAR* CurPos = NULL;
 699 static TCHAR* CurTokenName = NULL;
 700 static int CurTokenType;
 701 static int MaxTokenSize = -1;
 702 
 703 /* Copy token from buffer to Token variable */
 704 static void SetToken(int type, TCHAR* start, TCHAR* end) {
 705     int len = end - start;
 706     if (len > MaxTokenSize) {
 707         if (CurTokenName != NULL) free(CurTokenName);
 708         CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR));
 709         if (CurTokenName == NULL) {
 710             return;
 711         }
 712         MaxTokenSize = len;
 713     }
 714 
 715     CurTokenType = type;
 716     JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len);
 717     CurTokenName[len] = '\0';
 718 }
 719 
 720 /* Skip XML comments, doctypes, and prolog tags */
 721 static TCHAR* SkipFilling(void) {
 722     TCHAR *q = CurPos;
 723 
 724     /* Skip white space and comment sections */
 725     do {
 726         q = CurPos;
 727         CurPos = SkipWhiteSpace(CurPos);
 728         CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */
 729         CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */
 730         CurPos = SkipXMLProlog(CurPos); /* <? ... ?> directives */
 731     } while (CurPos != q);
 732 
 733     return CurPos;
 734 }
 735 
 736 /* Parses next token and initializes the global token variables above
 737    The tokennizer automatically skips comments (<!-- comment -->) and
 738    <! ... > directives.
 739  */
 740 static void GetNextToken(void) {
 741     TCHAR *p, *q;
 742 
 743     /* Skip white space and comment sections */
 744     p = SkipFilling();
 745 
 746     if (p == NULL || *p == '\0') {
 747         CurTokenType = TOKEN_EOF;
 748         return;
 749     } else if (p[0] == '<' && p[1] == '/') {
 750         /* TOKEN_END_TAG */
 751         q = SkipXMLName(p + 2);
 752         SetToken(TOKEN_END_TAG, p + 2, q);
 753         p = q;
 754     } else if (*p == '<') {
 755         /* TOKEN_BEGIN_TAG */
 756         q = SkipXMLName(p + 1);
 757         SetToken(TOKEN_BEGIN_TAG, p + 1, q);
 758         p = q;
 759     } else if (p[0] == '>') {
 760         CurTokenType = TOKEN_CLOSE_BRACKET;
 761         NEXT_CHAR(p);
 762     } else if (p[0] == '/' && p[1] == '>') {
 763         CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET;
 764         SKIP_CHARS(p, 2);
 765     } else {
 766         /* Search for end of data */
 767         q = p + 1;
 768         while (*q && *q != '<') {
 769             if (IsPCData(q)) {
 770                 q = SkipPCData(q);
 771             } else {
 772                 NEXT_CHAR(q);
 773             }
 774         }
 775         SetToken(TOKEN_PCDATA, p, q);
 776         /* Convert all entities inside token */
 777         ConvertBuiltInEntities(CurTokenName);
 778         p = q;
 779     }
 780     /* Advance pointer to beginning of next token */
 781     CurPos = p;
 782 }
 783 
 784 static XMLNode* CreateXMLNode(int type, TCHAR* name) {
 785     XMLNode* node;
 786     node = (XMLNode*) malloc(sizeof (XMLNode));
 787     if (node == NULL) {
 788         return NULL;
 789     }
 790     node->_type = type;
 791     node->_name = name;
 792     node->_next = NULL;
 793     node->_sub = NULL;
 794     node->_attributes = NULL;
 795     return node;
 796 }
 797 
 798 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) {
 799     XMLAttribute* attr;
 800     attr = (XMLAttribute*) malloc(sizeof (XMLAttribute));
 801     if (attr == NULL) {
 802         return NULL;
 803     }
 804     attr->_name = name;
 805     attr->_value = value;
 806     attr->_next = NULL;
 807     return attr;
 808 }
 809 
 810 XMLNode* ParseXMLDocument(TCHAR* buf) {
 811     XMLNode* root;
 812     int err_code = setjmp(jmpbuf);
 813     switch (err_code) {
 814         case JMP_NO_ERROR:
 815 #ifndef _UNICODE
 816             /* Remove UTF-8 encoding from buffer */
 817             RemoveNonAsciiUTF8FromBuffer(buf);
 818 #endif
 819 
 820             /* Get first Token */
 821             CurPos = buf;
 822             GetNextToken();
 823 
 824             /* Parse document*/
 825             root = ParseXMLElement();
 826             break;
 827         case JMP_OUT_OF_RANGE:
 828             /* cleanup: */
 829             if (root_node != NULL) {
 830                 FreeXMLDocument(root_node);
 831                 root_node = NULL;
 832             }
 833             if (CurTokenName != NULL) free(CurTokenName);
 834             fprintf(stderr, "Error during parsing jnlp file...\n");
 835             exit(-1);
 836             break;
 837         default:
 838             root = NULL;
 839             break;
 840     }
 841 
 842     return root;
 843 }
 844 
 845 static XMLNode* ParseXMLElement(void) {
 846     XMLNode* node = NULL;
 847     XMLNode* subnode = NULL;
 848     XMLNode* nextnode = NULL;
 849     XMLAttribute* attr = NULL;
 850 
 851     if (CurTokenType == TOKEN_BEGIN_TAG) {
 852 
 853         /* Create node for new element tag */
 854         node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName));
 855         /* We need to save root node pointer to be able to cleanup
 856            if an error happens during parsing */
 857         if (!root_node) {
 858             root_node = node;
 859         }
 860         /* Parse attributes. This section eats a all input until
 861            EOF, a > or a /> */
 862         attr = ParseXMLAttribute();
 863         while (attr != NULL) {
 864             attr->_next = node->_attributes;
 865             node->_attributes = attr;
 866             attr = ParseXMLAttribute();
 867         }
 868 
 869         /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a
 870          * TOKEN_EMPTY_CLOSE_BRACKET */
 871         GetNextToken();
 872 
 873         if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) {
 874             GetNextToken();
 875             /* We are done with the sublevel - fall through to continue */
 876             /* parsing tags at the same level */
 877         } else if (CurTokenType == TOKEN_CLOSE_BRACKET) {
 878             GetNextToken();
 879 
 880             /* Parse until end tag if found */
 881             node->_sub = ParseXMLElement();
 882 
 883             if (CurTokenType == TOKEN_END_TAG) {
 884                 /* Find closing bracket '>' for end tag */
 885                 do {
 886                     GetNextToken();
 887                 } while (CurTokenType != TOKEN_EOF &&
 888                         CurTokenType != TOKEN_CLOSE_BRACKET);
 889                 GetNextToken();
 890             }
 891         }
 892 
 893         /* Continue parsing rest on same level */
 894         if (CurTokenType != TOKEN_EOF) {
 895             /* Parse rest of stream at same level */
 896             node->_next = ParseXMLElement();
 897         }
 898         return node;
 899 
 900     } else if (CurTokenType == TOKEN_PCDATA) {
 901         /* Create node for pcdata */
 902         node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName));
 903         /* We need to save root node pointer to be able to cleanup
 904            if an error happens during parsing */
 905         if (!root_node) {
 906             root_node = node;
 907         }
 908         GetNextToken();
 909         return node;
 910     }
 911 
 912     /* Something went wrong. */
 913     return NULL;
 914 }
 915 
 916 /* Parses an XML attribute. */
 917 static XMLAttribute* ParseXMLAttribute(void) {
 918     TCHAR* q = NULL;
 919     TCHAR* name = NULL;
 920     TCHAR* PrevPos = NULL;
 921 
 922     do {
 923         /* We need to check this condition to avoid endless loop
 924            in case if an error happend during parsing. */
 925         if (PrevPos == CurPos) {
 926             if (name != NULL) {
 927                 free(name);
 928                 name = NULL;
 929             }
 930 
 931             return NULL;
 932         }
 933 
 934         PrevPos = CurPos;
 935 
 936         /* Skip whitespace etc. */
 937         SkipFilling();
 938 
 939         /* Check if we are done witht this attribute section */
 940         if (CurPos[0] == '\0' ||
 941                 CurPos[0] == '>' ||
 942                 (CurPos[0] == '/' && CurPos[1] == '>')) {
 943 
 944             if (name != NULL) {
 945                 free(name);
 946                 name = NULL;
 947             }
 948 
 949             return NULL;
 950         }
 951 
 952         /* Find end of name */
 953         q = CurPos;
 954         while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q);
 955 
 956         SetToken(TOKEN_UNKNOWN, CurPos, q);
 957         if (name) {
 958             free(name);
 959             name = NULL;
 960         }
 961         name = JPACKAGE_STRDUP(CurTokenName);
 962 
 963         /* Skip any whitespace */
 964         CurPos = q;
 965         CurPos = SkipFilling();
 966 
 967         /* Next TCHARacter must be '=' for a valid attribute.
 968            If it is not, this is really an error.
 969            We ignore this, and just try to parse an attribute
 970            out of the rest of the string.
 971          */
 972     } while (*CurPos != '=');
 973 
 974     NEXT_CHAR(CurPos);
 975     CurPos = SkipWhiteSpace(CurPos);
 976     /* Parse CDATA part of attribute */
 977     if ((*CurPos == '\"') || (*CurPos == '\'')) {
 978         TCHAR quoteChar = *CurPos;
 979         q = ++CurPos;
 980         while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q);
 981         SetToken(TOKEN_CDATA, CurPos, q);
 982         CurPos = q + 1;
 983     } else {
 984         q = CurPos;
 985         while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q);
 986         SetToken(TOKEN_CDATA, CurPos, q);
 987         CurPos = q;
 988     }
 989 
 990     //Note: no need to free name and CurTokenName duplicate; they're assigned
 991     // to an XMLAttribute structure in CreateXMLAttribute
 992 
 993     return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName));
 994 }
 995 
 996 void FreeXMLDocument(XMLNode* root) {
 997     if (root == NULL) return;
 998     FreeXMLDocument(root->_sub);
 999     FreeXMLDocument(root->_next);
1000     FreeXMLAttribute(root->_attributes);
1001     free(root->_name);
1002     free(root);
1003 }
1004 
1005 static void FreeXMLAttribute(XMLAttribute* attr) {
1006     if (attr == NULL) return;
1007     free(attr->_name);
1008     free(attr->_value);
1009     FreeXMLAttribute(attr->_next);
1010     free(attr);
1011 }
1012 
1013 /* Find element at current level with a given name */
1014 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) {
1015     if (root == NULL) return NULL;
1016 
1017     if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) {
1018         return root;
1019     }
1020 
1021     return FindXMLChild(root->_next, name);
1022 }
1023 
1024 /* Search for an attribute with the given name and returns the contents. Returns NULL if
1025  * attribute is not found
1026  */
1027 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) {
1028     if (attr == NULL) return NULL;
1029     if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value;
1030     return FindXMLAttribute(attr->_next, name);
1031 }
1032 
1033 void PrintXMLDocument(XMLNode* node, int indt) {
1034     if (node == NULL) return;
1035 
1036     if (node->_type == xmlTagType) {
1037         JPACKAGE_PRINTF(_T("\n"));
1038         indent(indt);
1039         JPACKAGE_PRINTF(_T("<%s"), node->_name);
1040         PrintXMLAttributes(node->_attributes);
1041         if (node->_sub == NULL) {
1042             JPACKAGE_PRINTF(_T("/>\n"));
1043         } else {
1044             JPACKAGE_PRINTF(_T(">"));
1045             PrintXMLDocument(node->_sub, indt + 1);
1046             indent(indt);
1047             JPACKAGE_PRINTF(_T("</%s>"), node->_name);
1048         }
1049     } else {
1050         JPACKAGE_PRINTF(_T("%s"), node->_name);
1051     }
1052     PrintXMLDocument(node->_next, indt);
1053 }
1054 
1055 static void PrintXMLAttributes(XMLAttribute* attr) {
1056     if (attr == NULL) return;
1057 
1058     JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value);
1059     PrintXMLAttributes(attr->_next);
1060 }
1061 
1062 static void indent(int indt) {
1063     int i;
1064     for (i = 0; i < indt; i++) {
1065         JPACKAGE_PRINTF(_T("  "));
1066     }
1067 }
1068 
1069 const TCHAR *CDStart = _T("<![CDATA[");
1070 const TCHAR *CDEnd = _T("]]>");
1071 
1072 static TCHAR* SkipPCData(TCHAR *p) {
1073     TCHAR *end = JPACKAGE_STRSTR(p, CDEnd);
1074     if (end != NULL) {
1075         return end + sizeof (CDEnd);
1076     }
1077     return (++p);
1078 }
1079 
1080 static int IsPCData(TCHAR *p) {
1081     const int size = sizeof (CDStart);
1082     return (JPACKAGE_STRNCMP(CDStart, p, size) == 0);
1083 }