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