1 /* 2 * Copyright (c) 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jnlp.converter; 25 26 import java.io.BufferedWriter; 27 import java.io.File; 28 import java.io.FileNotFoundException; 29 import java.io.FileOutputStream; 30 import java.io.FileWriter; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.PrintWriter; 34 import java.net.HttpURLConnection; 35 import java.net.URI; 36 import java.net.URL; 37 import java.nio.file.Files; 38 import java.nio.file.Path; 39 import java.util.ArrayList; 40 import java.util.Enumeration; 41 import java.util.List; 42 import java.util.jar.JarEntry; 43 import java.util.jar.JarFile; 44 import jnlp.converter.parser.JNLPDesc; 45 import jnlp.converter.parser.JNLPDesc.AssociationDesc; 46 import jnlp.converter.parser.JNLPDesc.IconDesc; 47 import jnlp.converter.parser.ResourcesDesc.JARDesc; 48 import jnlp.converter.parser.XMLFormat; 49 50 public class JNLPConverter { 51 52 private final Options options; 53 private JNLPDesc jnlpd = null; 54 private final List<String> launchArgs = new ArrayList<>(); 55 56 private String downloadFolder = null; 57 private String jnlpDownloadFolder = null; 58 private static String jnlpDownloadFolderStatic; 59 private String jarDownloadFolder = null; 60 private String iconDownloadFolder = null; 61 private String propDownloadFolder = null; 62 63 private static String jpackagerPath = null; 64 65 private static boolean markFileToDelete = false; 66 67 private static final String FA_EXTENSIONS = "extension"; 68 private static final String FA_CONTENT_TYPE = "mime-type"; 69 private static final String FA_DESCRIPTION = "description"; 70 private static final String FA_ICON = "icon"; 71 72 public JNLPConverter(Options options) { 73 this.options = options; 74 jnlpDownloadFolderStatic = getJnlpDownloadFolder(); 75 markFileToDelete = (options.keep() == null); 76 } 77 78 public String [] getLaunchArgs() { 79 return launchArgs.toArray(new String[0]); 80 } 81 82 public void convert() { 83 try { 84 loadJNLPDesc(); 85 downloadResources(); 86 validate(); 87 buildLaunchArgs(); 88 saveLaunchArgs(); 89 runJPackager(); 90 } catch (Exception ex) { 91 Log.error(ex.getLocalizedMessage()); 92 } 93 } 94 95 private JNLPDesc getJNLPD(String jnlp) throws Exception { 96 URL codebase = getCodeBase(jnlp); 97 byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp); 98 return XMLFormat.parse(bits, codebase, jnlp); 99 } 100 101 private void loadJNLPDesc() throws Exception { 102 String jnlp = options.getJNLP(); 103 jnlpd = getJNLPD(jnlp); 104 105 // Check for required options in case of FX 106 if (jnlpd.isFXApp()) { 107 if (!options.isRuntimeImageSet()) { 108 throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackager option --runtime-image (using --jpackager-options)."); 109 } 110 } 111 112 // Check href. It can be same as URL we provided or new one 113 // if JNLP has different href or codebase. We assume that 114 // XMLFormat.parse() will handle any errors in href and codebase 115 // correctly. 116 String href = jnlpd.getHref(); 117 if (href != null && !href.equalsIgnoreCase(jnlp)) { 118 if (href.startsWith("file:")) { 119 URI hrefURI = new URI(href); 120 URI jnlpURI = new URI(jnlp); 121 122 String hrefPath = hrefURI.getPath(); 123 String jnlpPath = jnlpURI.getPath(); 124 125 if (!hrefPath.equalsIgnoreCase(jnlpPath)) { 126 jnlp = href; 127 jnlpd = getJNLPD(jnlp); 128 } 129 } else { 130 jnlp = href; 131 jnlpd = getJNLPD(jnlp); 132 } 133 } 134 135 if (jnlpd.getName() == null) { 136 jnlpd.setName(getNameFromURL(jnlp)); 137 } 138 } 139 140 private static String getNameFromURL(String url) throws IOException { 141 int index; 142 int index1 = url.lastIndexOf('/'); 143 int index2 = url.lastIndexOf('\\'); 144 145 if (index1 >= index2) { 146 index = index1; 147 } else { 148 index = index2; 149 } 150 151 if (index != -1) { 152 String name = url.substring(index + 1, url.length()); 153 if (name.endsWith(".jnlp")) { 154 return name.substring(0, name.length() - 5); 155 } 156 } 157 158 return null; 159 } 160 161 private URL getCodeBase(String jnlp) throws Exception { 162 int index = jnlp.lastIndexOf('/'); 163 if (index != -1) { 164 if (HTTPHelper.isHTTPUrl(jnlp)) { 165 return new URL(jnlp.substring(0, index + 1)); 166 } else { 167 String codeBasePath = jnlp.substring(0, index); 168 if (!codeBasePath.endsWith("/")) { 169 codeBasePath += "/"; 170 } 171 return new URI(codeBasePath).toURL(); 172 } 173 } 174 175 return null; 176 } 177 178 public static void markFileToDelete(String file) { 179 if (file == null || file.isEmpty()) { 180 return; 181 } 182 183 if (markFileToDelete) { 184 try { 185 File f = new File(file); 186 f.deleteOnExit(); 187 } catch (Exception e) { 188 // Print exception, but do not fail conversion. 189 Log.warning(e.getLocalizedMessage()); 190 } 191 } 192 } 193 194 public static void deleteFile(String file) { 195 try { 196 File f = new File(file); 197 f.delete(); 198 } catch (Exception e) { 199 Log.warning(e.getLocalizedMessage()); 200 } 201 } 202 203 private void downloadResources() throws Exception { 204 List<JARDesc> jars = jnlpd.getResources(); 205 for (JARDesc jar : jars) { 206 if (jar.getVersion() != null) { 207 if (!jnlpd.isVersionEnabled()) { 208 throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true."); 209 } 210 } 211 212 String destFile = null; 213 if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) { 214 if (jar.getVersion() != null) { 215 try { 216 destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 217 } catch (HTTPHelperException ex) { 218 if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { 219 System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation()); 220 System.out.println(ex.getMessage()); 221 System.out.println("Downloading " + jar.getLocation() + " instead."); 222 destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 223 } else { 224 throw ex; 225 } 226 } 227 } else { 228 destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 229 } 230 markFileToDelete(destFile); 231 } else { 232 if (jar.getVersion() != null) { 233 try { 234 destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 235 } catch (FileNotFoundException ex) { 236 System.out.println("Error copying versioned JAR from " + jar.getVersionLocation()); 237 System.out.println(ex.getMessage()); 238 System.out.println("Copying " + jar.getLocation() + " instead."); 239 destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 240 } 241 } else { 242 destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); 243 } 244 markFileToDelete(destFile); 245 } 246 247 if (jar.isNativeLib()) { 248 unpackNativeLib(destFile); 249 deleteFile(destFile); 250 } else { 251 jnlpd.addFile(jar.getName()); 252 } 253 } 254 255 IconDesc icon = jnlpd.getIcon(); 256 if (icon != null) { 257 String destFile; 258 259 if (HTTPHelper.isHTTPUrl(icon.getLocation())) { 260 destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation())); 261 } else { 262 destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation())); 263 } 264 265 markFileToDelete(destFile); 266 icon.setLocalLocation(destFile); 267 } 268 269 AssociationDesc [] associations = jnlpd.getAssociations(); 270 if (associations != null) { 271 for (AssociationDesc association : associations) { 272 if (association.getIconUrl() != null) { 273 String destFile; 274 if (HTTPHelper.isHTTPUrl(association.getIconUrl())) { 275 destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl())); 276 } else { 277 destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl())); 278 } 279 280 markFileToDelete(destFile); 281 association.setIconLocalLocation(destFile); 282 } 283 } 284 } 285 } 286 287 public void unpackNativeLib(String file) throws IOException { 288 try (JarFile jarFile = new JarFile(file)) { 289 Enumeration entries = jarFile.entries(); 290 291 while (entries.hasMoreElements()) { 292 JarEntry entry = (JarEntry) entries.nextElement(); 293 294 // Skip directories 295 if (entry.isDirectory()) { 296 continue; 297 } 298 299 String entryName = entry.getName(); 300 // Skip anything in sub-directories 301 if (entryName.contains("\\") || entryName.contains("/")) { 302 continue; 303 } 304 305 // Skip anything not ending with .dll, .dylib or .so 306 if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) { 307 continue; 308 } 309 310 File destFile = new File(getJarDownloadFolder(), entryName); 311 if (destFile.exists()) { 312 Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + "."); 313 continue; 314 } 315 316 InputStream inputStream = jarFile.getInputStream(entry); 317 FileOutputStream outputStream = new FileOutputStream(destFile); 318 319 byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE]; 320 int length; 321 do { 322 length = inputStream.read(buffer); 323 if (length > 0) { 324 outputStream.write(buffer, 0, length); 325 } 326 } while (length > 0); 327 328 jnlpd.addFile(entryName); 329 } 330 } 331 } 332 333 private void validate() { 334 if (jnlpd.getMainJar() == null) { 335 Log.error("Cannot find main jar"); 336 } 337 338 if (jnlpd.getMainClass() == null) { 339 Log.error("Cannot find main class"); 340 } 341 } 342 343 private void addLaunchArg(String arg, List<String> launchArgs) { 344 if (arg != null && !arg.isEmpty()) { 345 if (!options.isOptionPresent(arg)){ 346 launchArgs.add(arg); 347 } else { 348 Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options"); 349 } 350 } 351 } 352 353 private void addLaunchArg(String arg, String value, List<String> launchArgs) { 354 if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) { 355 if (!options.isOptionPresent(arg)){ 356 launchArgs.add(arg); 357 launchArgs.add(value); 358 } else { 359 Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options"); 360 } 361 } 362 } 363 364 private void displayLaunchArgs() { 365 if (Log.isVerbose()) { 366 System.out.println(); 367 System.out.println("jpackager launch arguments (each argument starts on new line):"); 368 launchArgs.forEach((arg) -> { 369 System.out.println(arg); 370 }); 371 } 372 } 373 374 private static int fileAssociationsCount = 0; 375 private String getFileAssociationsFile() { 376 String file = getPropDownloadFolder(); 377 file += File.separator; 378 file += "fileAssociation"; 379 file += String.valueOf(fileAssociationsCount); 380 file += ".properties"; 381 382 fileAssociationsCount++; 383 384 return file; 385 } 386 387 private void buildLaunchArgs() { 388 if (options.createImage()) { 389 addLaunchArg("create-image", launchArgs); 390 } else if (options.createInstaller()) { 391 if (options.getInstallerType() == null) { 392 addLaunchArg("create-installer", launchArgs); 393 } else { 394 addLaunchArg("create-installer", options.getInstallerType(), launchArgs); 395 } 396 } 397 398 // Set verbose for jpackager if it is set for us. 399 if (options.verbose()) { 400 addLaunchArg("--verbose", launchArgs); 401 } 402 403 addLaunchArg("--input", getJarDownloadFolder(), launchArgs); 404 addLaunchArg("--output", options.getOutput(), launchArgs); 405 addLaunchArg("--name", jnlpd.getName(), launchArgs); 406 addLaunchArg("--version", jnlpd.getVersion(), launchArgs); 407 addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs); 408 addLaunchArg("--description", jnlpd.getDescription(), launchArgs); 409 addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs); 410 addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs); 411 addLaunchArg("--class", jnlpd.getMainClass(), launchArgs); 412 413 addFiles(launchArgs); 414 addArguments(launchArgs); 415 addJVMArgs(launchArgs); 416 417 if (jnlpd.isDesktopHint()) { 418 if (Platform.isWindows()) { 419 addLaunchArg("--win-shortcut", launchArgs); 420 } else { 421 Log.warning("Ignoring shortcut hint, since it is not supported on current platform."); 422 } 423 } 424 425 if (jnlpd.isMenuHint()) { 426 if (Platform.isWindows()) { 427 addLaunchArg("--win-menu", launchArgs); 428 addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs); 429 } else { 430 Log.warning("Ignoring menu hint, since it is not supported on current platform."); 431 } 432 } 433 434 AssociationDesc [] associations = jnlpd.getAssociations(); 435 if (associations != null) { 436 for (AssociationDesc association : associations) { 437 String file = getFileAssociationsFile(); 438 markFileToDelete(file); 439 440 try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { 441 if (association.getExtensions() != null && association.getMimeType() != null) { 442 out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions())); 443 out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType())); 444 445 if (association.getMimeDescription() != null) { 446 out.println(FA_DESCRIPTION + "=" + association.getMimeDescription()); 447 } 448 449 if (association.getIconLocalLocation() != null) { 450 out.println(FA_ICON + "=" + quote(association.getIconLocalLocation())); 451 } 452 453 addLaunchArg("--file-associations", file, launchArgs); 454 } 455 } catch (Exception ex) { 456 Log.warning(ex.toString()); 457 if (association.getExtensions() != null) { 458 Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above."); 459 } 460 } 461 } 462 } 463 464 // Add options from --jpackager-options 465 List<String> jpackagerOptions = options.getJPackagerOptions(); 466 jpackagerOptions.forEach((option) -> { 467 launchArgs.add(option); 468 }); 469 470 displayLaunchArgs(); 471 } 472 473 private String getCommandFileName() { 474 Platform platform = Platform.getPlatform(); 475 switch (platform) { 476 case WINDOWS: 477 return "run_jpackager.bat"; 478 case LINUX: 479 return "run_jpackager.sh"; 480 case MAC: 481 return "run_jpackager.sh"; 482 default: 483 Log.error("Cannot determine platform type."); 484 return ""; 485 } 486 } 487 488 private void saveLaunchArgs() { 489 if (options.keep() != null) { 490 File keepFolder = new File(options.keep()); 491 String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName(); 492 try (PrintWriter out = new PrintWriter(cmdFile)) { 493 out.print(getJPackagerPath()); 494 launchArgs.forEach((arg) -> { 495 out.print(" "); 496 497 if (arg.contains(" ")) { 498 int len = arg.length(); 499 if (len >= 1) { 500 if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') { 501 out.print("\"" + arg + "\""); 502 } else { 503 if (Platform.isWindows()) { 504 out.print(arg); 505 } else { 506 arg = escapeQuote(arg); 507 out.print("\"" + arg + "\""); 508 } 509 } 510 } 511 } else { 512 out.print(arg); 513 } 514 }); 515 } catch (FileNotFoundException ex) { 516 Log.error("Cannot save file with command line: " + ex.getLocalizedMessage()); 517 } 518 } 519 } 520 521 private void runJPackager() { 522 List<String> command = new ArrayList<>(); 523 command.add(getJPackagerPath()); 524 command.addAll(launchArgs); 525 526 ProcessBuilder builder = new ProcessBuilder(); 527 builder.inheritIO(); 528 builder.command(command); 529 530 try { 531 Process process = builder.start(); 532 int exitCode = process.waitFor(); 533 if (exitCode != 0) { 534 Log.warning("jpackager retrun non zero code: " + exitCode); 535 } 536 } catch (IOException | InterruptedException ex) { 537 Log.error(ex.getMessage()); 538 } 539 } 540 541 private void addFileList(String arg, List<String> filesToAdd, List<String> launchArgs) { 542 if (filesToAdd.isEmpty()) { 543 return; 544 } 545 546 String filesArg = ""; 547 for (int i = 0; i < filesToAdd.size(); i++) { 548 filesArg += quote(filesToAdd.get(i)); 549 if ((i + 1) != filesToAdd.size()) { 550 filesArg += File.pathSeparator; 551 } 552 } 553 554 launchArgs.add(arg); 555 launchArgs.add(filesArg); 556 } 557 558 private void addFiles(List<String> launchArgs) { 559 addFileList("--files", jnlpd.getFiles(), launchArgs); 560 } 561 562 private void addArguments(List<String> launchArgs) { 563 List<String> arguments = jnlpd.getArguments(); 564 if (arguments.isEmpty()) { 565 return; 566 } 567 568 String argsStr = ""; 569 for (int i = 0; i < arguments.size(); i++) { 570 String arg = arguments.get(i); 571 argsStr += quote(arg); 572 if ((i + 1) != arguments.size()) { 573 argsStr += " "; 574 } 575 } 576 577 launchArgs.add("--arguments"); 578 if (Platform.isWindows()) { 579 if (argsStr.contains(" ")) { 580 if (argsStr.contains("\"")) { 581 argsStr = escapeQuote(argsStr); 582 } 583 argsStr = "\"" + argsStr + "\""; 584 } 585 } 586 launchArgs.add(argsStr); 587 } 588 589 private void addJVMArgs(List<String> launchArgs) { 590 List<String> jvmArgs = jnlpd.getVMArgs(); 591 if (jvmArgs.isEmpty()) { 592 return; 593 } 594 595 String jvmArgsStr = ""; 596 for (int i = 0; i < jvmArgs.size(); i++) { 597 String arg = jvmArgs.get(i); 598 jvmArgsStr += quote(arg); 599 if ((i + 1) != jvmArgs.size()) { 600 jvmArgsStr += " "; 601 } 602 } 603 604 launchArgs.add("--jvm-args"); 605 if (Platform.isWindows()) { 606 if (jvmArgsStr.contains(" ")) { 607 if (jvmArgsStr.contains("\"")) { 608 jvmArgsStr = escapeQuote(jvmArgsStr); 609 } 610 jvmArgsStr = "\"" + jvmArgsStr + "\""; 611 } 612 } 613 launchArgs.add(jvmArgsStr); 614 } 615 616 private String quote(String in) { 617 if (in == null) { 618 return null; 619 } 620 621 if (in.isEmpty()) { 622 return ""; 623 } 624 625 if (!in.contains("=")) { 626 // Not a property 627 if (in.contains(" ")) { 628 in = escapeQuote(in); 629 return "\"" + in + "\""; 630 } 631 return in; 632 } 633 634 if (!in.contains(" ")) { 635 return in; // No need to quote 636 } 637 638 int paramIndex = in.indexOf("="); 639 if (paramIndex <= 0) { 640 return in; // Something wrong, just skip quoting 641 } 642 643 String param = in.substring(0, paramIndex); 644 String value = in.substring(paramIndex + 1); 645 646 if (value.length() == 0) { 647 return in; // No need to quote 648 } 649 650 value = escapeQuote(value); 651 652 return param + "=" + "\"" + value + "\""; 653 } 654 655 private String escapeQuote(String in) { 656 if (in == null) { 657 return null; 658 } 659 660 if (in.isEmpty()) { 661 return ""; 662 } 663 664 if (in.contains("\"")) { 665 // Use code points to preserve non-ASCII chars 666 StringBuilder sb = new StringBuilder(); 667 int codeLen = in.codePointCount(0, in.length()); 668 for (int i = 0; i < codeLen; i++) { 669 int code = in.codePointAt(i); 670 // Note: No need to escape '\' on Linux or OS X. 671 // jpackager expects us to pass arguments and properties with quotes and spaces as a map 672 // with quotes being escaped with additional \ for internal quotes. 673 // So if we want two properties below: 674 // -Djnlp.Prop1=Some "Value" 1 675 // -Djnlp.Prop2=Some Value 2 676 // jpackager will need: 677 // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\"" 678 // but since we using ProcessBuilder to run jpackager we will need to escape 679 // our escape symbols as well, so we will need to pass string below to ProcessBuilder: 680 // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\"" 681 switch (code) { 682 case '"': 683 // " -> \" -> \\\" 684 if (i == 0 || in.codePointAt(i - 1) != '\\') { 685 if (Platform.isWindows()) { 686 sb.appendCodePoint('\\'); 687 sb.appendCodePoint('\\'); 688 } 689 sb.appendCodePoint('\\'); 690 sb.appendCodePoint(code); 691 } 692 break; 693 case '\\': 694 // We need to escape already escaped symbols as well 695 if ((i + 1) < codeLen) { 696 int nextCode = in.codePointAt(i + 1); 697 if (nextCode == '"') { 698 // \" -> \\\" 699 sb.appendCodePoint('\\'); 700 sb.appendCodePoint('\\'); 701 sb.appendCodePoint('\\'); 702 sb.appendCodePoint(nextCode); 703 } else { 704 sb.appendCodePoint('\\'); 705 sb.appendCodePoint(code); 706 } 707 } else { 708 if (Platform.isWindows()) { 709 sb.appendCodePoint('\\'); 710 } 711 sb.appendCodePoint(code); 712 } 713 break; 714 default: 715 sb.appendCodePoint(code); 716 break; 717 } 718 } 719 return sb.toString(); 720 } 721 722 return in; 723 } 724 725 public synchronized String getDownloadFolder() { 726 if (downloadFolder == null) { 727 try { 728 File file; 729 if (options.keep() == null) { 730 Path path = Files.createTempDirectory("JNLPConverter"); 731 file = path.toFile(); 732 file.deleteOnExit(); 733 } else { 734 file = new File(options.keep()); 735 if (!file.exists()) { 736 file.mkdir(); 737 } 738 } 739 740 downloadFolder = file.getAbsolutePath(); 741 } catch (IOException e) { 742 Log.error(e.getLocalizedMessage()); 743 } 744 } 745 746 return downloadFolder; 747 } 748 749 public final synchronized String getJnlpDownloadFolder() { 750 if (jnlpDownloadFolder == null) { 751 File file = new File(getDownloadFolder() + File.separator + "jnlp"); 752 file.mkdir(); 753 markFileToDelete(getDownloadFolder() + File.separator + "jnlp"); 754 jnlpDownloadFolder = file.getAbsolutePath(); 755 } 756 757 return jnlpDownloadFolder; 758 } 759 760 public static String getJnlpDownloadFolderStatic() { 761 return jnlpDownloadFolderStatic; 762 } 763 764 public synchronized String getJarDownloadFolder() { 765 if (jarDownloadFolder == null) { 766 File file = new File(getDownloadFolder() + File.separator + "jar"); 767 file.mkdir(); 768 markFileToDelete(getDownloadFolder() + File.separator + "jar"); 769 jarDownloadFolder = file.getAbsolutePath(); 770 } 771 772 return jarDownloadFolder; 773 } 774 775 public synchronized String getIconDownloadFolder() { 776 if (iconDownloadFolder == null) { 777 File file = new File(getDownloadFolder() + File.separator + "icon"); 778 file.mkdir(); 779 markFileToDelete(getDownloadFolder() + File.separator + "icon"); 780 iconDownloadFolder = file.getAbsolutePath(); 781 } 782 783 return iconDownloadFolder; 784 } 785 786 public synchronized String getPropDownloadFolder() { 787 if (propDownloadFolder == null) { 788 File file = new File(getDownloadFolder() + File.separator + "prop"); 789 file.mkdir(); 790 markFileToDelete(getDownloadFolder() + File.separator + "prop"); 791 propDownloadFolder = file.getAbsolutePath(); 792 } 793 794 return propDownloadFolder; 795 } 796 797 public synchronized static String getJPackagerPath() { 798 if (jpackagerPath == null) { 799 jpackagerPath = System.getProperty("java.home"); 800 jpackagerPath += File.separator; 801 jpackagerPath += "bin"; 802 jpackagerPath += File.separator; 803 804 Platform platform = Platform.getPlatform(); 805 switch (platform) { 806 case WINDOWS: 807 jpackagerPath += "jpackager.exe"; 808 break; 809 case LINUX: 810 jpackagerPath += "jpackager"; 811 break; 812 case MAC: 813 jpackagerPath += "jpackager"; 814 break; 815 default: 816 Log.error("Cannot determine platform type."); 817 break; 818 } 819 820 Log.verbose("jpackager: " + jpackagerPath); 821 } 822 823 return jpackagerPath; 824 } 825 826 public static String getIconFormat(String icon) { 827 // GIF, JPEG, ICO, or PNG 828 if (icon.toLowerCase().endsWith(".gif")) { 829 return "GIF"; 830 } else if (icon.toLowerCase().endsWith(".jpg")) { 831 return "JPEG"; 832 } else if (icon.toLowerCase().endsWith(".ico")) { 833 return "ICO"; 834 } else if (icon.toLowerCase().endsWith(".png")) { 835 return "PNG"; 836 } 837 838 return "UNKNOWN"; 839 } 840 841 public static boolean isIconSupported(String icon) { 842 Platform platform = Platform.getPlatform(); 843 switch (platform) { 844 case WINDOWS: 845 if (icon.endsWith(".ico")) { 846 return true; 847 } else { 848 Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + "."); 849 return false; 850 } 851 case LINUX: 852 if (icon.endsWith(".png")) { 853 return true; 854 } else { 855 Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + "."); 856 return false; 857 } 858 case MAC: 859 Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + "."); 860 return false; 861 } 862 863 return false; 864 } 865 }