001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.quicksetup.util; 028 029import static org.forgerock.util.Utils.*; 030import static org.opends.admin.ads.util.ConnectionUtils.*; 031import static org.opends.messages.QuickSetupMessages.*; 032import static org.opends.quicksetup.Installation.*; 033import static org.opends.server.util.DynamicConstants.*; 034 035import static com.forgerock.opendj.cli.Utils.*; 036import static com.forgerock.opendj.util.OperatingSystem.*; 037 038import java.io.BufferedOutputStream; 039import java.io.BufferedReader; 040import java.io.ByteArrayOutputStream; 041import java.io.File; 042import java.io.FileOutputStream; 043import java.io.FileReader; 044import java.io.FileWriter; 045import java.io.IOException; 046import java.io.InputStream; 047import java.io.InputStreamReader; 048import java.io.PrintStream; 049import java.io.PrintWriter; 050import java.io.RandomAccessFile; 051import java.net.InetAddress; 052import java.text.SimpleDateFormat; 053import java.util.ArrayList; 054import java.util.Collection; 055import java.util.HashMap; 056import java.util.Hashtable; 057import java.util.LinkedHashSet; 058import java.util.List; 059import java.util.Locale; 060import java.util.Map; 061import java.util.Set; 062import java.util.TimeZone; 063 064import javax.naming.AuthenticationException; 065import javax.naming.CommunicationException; 066import javax.naming.NamingEnumeration; 067import javax.naming.NamingException; 068import javax.naming.NamingSecurityException; 069import javax.naming.NoPermissionException; 070import javax.naming.directory.SearchControls; 071import javax.naming.directory.SearchResult; 072import javax.naming.ldap.InitialLdapContext; 073import javax.naming.ldap.LdapName; 074import javax.net.ssl.HostnameVerifier; 075import javax.net.ssl.TrustManager; 076 077import org.forgerock.i18n.LocalizableMessage; 078import org.forgerock.i18n.LocalizableMessageBuilder; 079import org.forgerock.i18n.slf4j.LocalizedLogger; 080import org.forgerock.opendj.config.ManagedObjectDefinition; 081import org.forgerock.opendj.server.config.client.BackendCfgClient; 082import org.forgerock.opendj.server.config.server.BackendCfg; 083import org.opends.admin.ads.ADSContext; 084import org.opends.admin.ads.ReplicaDescriptor; 085import org.opends.admin.ads.ServerDescriptor; 086import org.opends.admin.ads.SuffixDescriptor; 087import org.opends.admin.ads.TopologyCacheException; 088import org.opends.admin.ads.util.ConnectionUtils; 089import org.opends.quicksetup.Constants; 090import org.opends.quicksetup.Installation; 091import org.opends.quicksetup.SecurityOptions; 092import org.opends.quicksetup.UserData; 093import org.opends.quicksetup.installer.AuthenticationData; 094import org.opends.quicksetup.installer.DataReplicationOptions; 095import org.opends.quicksetup.installer.NewSuffixOptions; 096import org.opends.quicksetup.installer.SuffixesToReplicateOptions; 097import org.opends.quicksetup.ui.UIFactory; 098import org.opends.server.tools.BackendTypeHelper; 099import org.opends.server.util.SetupUtils; 100import org.opends.server.util.StaticUtils; 101 102import com.forgerock.opendj.cli.ArgumentConstants; 103import com.forgerock.opendj.cli.ClientException; 104 105/** 106 * This class provides some static convenience methods of different nature. 107 */ 108public class Utils 109{ 110 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 111 112 private Utils() {} 113 114 private static final int BUFFER_SIZE = 1024; 115 private static final int MAX_LINE_WIDTH = 80; 116 117 /** Chars that require special treatment when passing them to command-line. */ 118 private static final char[] CHARS_TO_ESCAPE = 119 { ' ', '\t', '\n', '|', ';', '<', '>', '(', ')', '$', '`', '\\', '"', '\'' }; 120 121 /** The class name that contains the control panel customizations for products. */ 122 private static final String CUSTOMIZATION_CLASS_NAME = "org.opends.server.util.ReleaseDefinition"; 123 124 /** 125 * Returns <CODE>true</CODE> if the provided port is free and we can use it, 126 * <CODE>false</CODE> otherwise. 127 * 128 * @param port 129 * the port we are analyzing. 130 * @return <CODE>true</CODE> if the provided port is free and we can use it, 131 * <CODE>false</CODE> otherwise. 132 */ 133 public static boolean canUseAsPort(int port) 134 { 135 return SetupUtils.canUseAsPort(port); 136 } 137 138 /** 139 * Returns <CODE>true</CODE> if the provided port is a privileged port, 140 * <CODE>false</CODE> otherwise. 141 * 142 * @param port 143 * the port we are analyzing. 144 * @return <CODE>true</CODE> if the provided port is a privileged port, 145 * <CODE>false</CODE> otherwise. 146 */ 147 public static boolean isPrivilegedPort(int port) 148 { 149 return SetupUtils.isPrivilegedPort(port); 150 } 151 152 /** 153 * Tells whether the provided java installation supports a given option or 154 * not. 155 * 156 * @param javaHome 157 * the java installation path. 158 * @param option 159 * the java option that we want to check. 160 * @param installPath 161 * the install path of the server. 162 * @return <CODE>true</CODE> if the provided java installation supports a 163 * given option and <CODE>false</CODE> otherwise. 164 */ 165 public static boolean supportsOption(String option, String javaHome, String installPath) 166 { 167 boolean supported = false; 168 logger.info(LocalizableMessage.raw("Checking if options " + option + " are supported with java home: " + javaHome)); 169 try 170 { 171 List<String> args = new ArrayList<>(); 172 String script; 173 String libPath = Utils.getPath(installPath, Installation.LIBRARIES_PATH_RELATIVE); 174 if (isWindows()) 175 { 176 script = Utils.getScriptPath(Utils.getPath(libPath, Installation.SCRIPT_UTIL_FILE_WINDOWS)); 177 } 178 else 179 { 180 script = Utils.getScriptPath(Utils.getPath(libPath, Installation.SCRIPT_UTIL_FILE_UNIX)); 181 } 182 args.add(script); 183 ProcessBuilder pb = new ProcessBuilder(args); 184 Map<String, String> env = pb.environment(); 185 env.put(SetupUtils.OPENDJ_JAVA_HOME, javaHome); 186 env.put("OPENDJ_JAVA_ARGS", option); 187 env.put("SCRIPT_UTIL_CMD", "set-full-environment-and-test-java"); 188 env.remove("OPENDJ_JAVA_BIN"); 189 // In windows by default the scripts ask the user to click on enter when 190 // they fail. Set this environment variable to avoid it. 191 if (isWindows()) 192 { 193 env.put("DO_NOT_PAUSE", "true"); 194 } 195 final Process process = pb.start(); 196 logger.info(LocalizableMessage.raw("launching " + args + " with env: " + env)); 197 InputStream is = process.getInputStream(); 198 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 199 String line; 200 boolean errorDetected = false; 201 while (null != (line = reader.readLine())) 202 { 203 logger.info(LocalizableMessage.raw("The output: " + line)); 204 if (line.contains("ERROR: The detected Java version")) 205 { 206 if (isWindows()) 207 { 208 // If we are running windows, the process get blocked waiting for 209 // user input. Just wait for a certain time to print the output 210 // in the logger and then kill the process. 211 Thread t = new Thread(new Runnable() 212 { 213 @Override 214 public void run() 215 { 216 try 217 { 218 Thread.sleep(3000); 219 // To see if the process is over, call the exitValue method. 220 // If it is not over, a IllegalThreadStateException. 221 process.exitValue(); 222 } 223 catch (Throwable t) 224 { 225 process.destroy(); 226 } 227 } 228 }); 229 t.start(); 230 } 231 errorDetected = true; 232 } 233 } 234 process.waitFor(); 235 int returnCode = process.exitValue(); 236 logger.info(LocalizableMessage.raw("returnCode: " + returnCode)); 237 supported = returnCode == 0 && !errorDetected; 238 logger.info(LocalizableMessage.raw("supported: " + supported)); 239 } 240 catch (Throwable t) 241 { 242 logger.warn(LocalizableMessage.raw("Error testing option " + option + " on " + javaHome, t)); 243 } 244 return supported; 245 } 246 247 /** 248 * Creates a new file attempting to create the parent directories if 249 * necessary. 250 * 251 * @param f 252 * File to create 253 * @return boolean indicating whether the file was created; false otherwise 254 * @throws IOException 255 * if something goes wrong 256 */ 257 public static boolean createFile(File f) throws IOException 258 { 259 boolean success = false; 260 if (f != null) 261 { 262 File parent = f.getParentFile(); 263 if (!parent.exists()) 264 { 265 parent.mkdirs(); 266 } 267 success = f.createNewFile(); 268 } 269 return success; 270 } 271 272 /** 273 * Returns the absolute path for the given parentPath and relativePath. 274 * 275 * @param parentPath 276 * the parent path. 277 * @param relativePath 278 * the relative path. 279 * @return the absolute path for the given parentPath and relativePath. 280 */ 281 public static String getPath(String parentPath, String relativePath) 282 { 283 return getPath(new File(new File(parentPath), relativePath)); 284 } 285 286 /** 287 * Returns the String that can be used to launch an script using Runtime.exec. 288 * This method is required because in Windows the script that contain a "=" in 289 * their path must be quoted. 290 * 291 * @param script 292 * the script name 293 * @return the absolute path for the given parentPath and relativePath. 294 */ 295 public static String getScriptPath(String script) 296 { 297 return SetupUtils.getScriptPath(script); 298 } 299 300 /** 301 * Returns the absolute path for the given file. It tries to get the canonical 302 * file path. If it fails it returns the string representation. 303 * 304 * @param f 305 * File to get the path 306 * @return the absolute path for the given file. 307 */ 308 public static String getPath(File f) 309 { 310 String path = null; 311 if (f != null) 312 { 313 try 314 { 315 /* 316 * Do a best effort to avoid having a relative representation (for 317 * instance to avoid having ../../../). 318 */ 319 f = f.getCanonicalFile(); 320 } 321 catch (IOException ioe) 322 { 323 /* 324 * This is a best effort to get the best possible representation of the 325 * file: reporting the error is not necessary. 326 */ 327 } 328 path = f.toString(); 329 } 330 return path; 331 } 332 333 /** 334 * Returns <CODE>true</CODE> if the first provided path is under the second 335 * path in the file system. 336 * 337 * @param descendant 338 * the descendant candidate path. 339 * @param path 340 * the path. 341 * @return <CODE>true</CODE> if the first provided path is under the second 342 * path in the file system; <code>false</code> otherwise or if either 343 * of the files are null 344 */ 345 public static boolean isDescendant(File descendant, File path) 346 { 347 boolean isDescendant = false; 348 if (descendant != null && path != null) 349 { 350 File parent = descendant.getParentFile(); 351 while (parent != null && !isDescendant) 352 { 353 isDescendant = path.equals(parent); 354 if (!isDescendant) 355 { 356 parent = parent.getParentFile(); 357 } 358 } 359 } 360 return isDescendant; 361 } 362 363 /** 364 * Returns <CODE>true</CODE> if the parent directory for the provided path 365 * exists and <CODE>false</CODE> otherwise. 366 * 367 * @param path 368 * the path that we are analyzing. 369 * @return <CODE>true</CODE> if the parent directory for the provided path 370 * exists and <CODE>false</CODE> otherwise. 371 */ 372 public static boolean parentDirectoryExists(String path) 373 { 374 boolean parentExists = false; 375 File f = new File(path); 376 File parentFile = f.getParentFile(); 377 if (parentFile != null) 378 { 379 parentExists = parentFile.isDirectory(); 380 } 381 return parentExists; 382 } 383 384 /** 385 * Returns <CODE>true</CODE> if the the provided path is a file and exists and 386 * <CODE>false</CODE> otherwise. 387 * 388 * @param path 389 * the path that we are analyzing. 390 * @return <CODE>true</CODE> if the the provided path is a file and exists and 391 * <CODE>false</CODE> otherwise. 392 */ 393 public static boolean fileExists(String path) 394 { 395 File f = new File(path); 396 return f.isFile(); 397 } 398 399 /** 400 * Returns <CODE>true</CODE> if the the provided path is a directory, exists 401 * and is not empty <CODE>false</CODE> otherwise. 402 * 403 * @param path 404 * the path that we are analyzing. 405 * @return <CODE>true</CODE> if the the provided path is a directory, exists 406 * and is not empty <CODE>false</CODE> otherwise. 407 */ 408 public static boolean directoryExistsAndIsNotEmpty(String path) 409 { 410 final File f = new File(path); 411 if (f.isDirectory()) 412 { 413 final String[] ch = f.list(); 414 return ch != null && ch.length > 0; 415 } 416 return false; 417 } 418 419 /** 420 * Returns <CODE>true</CODE> if the the provided string is a configuration DN 421 * and <CODE>false</CODE> otherwise. 422 * 423 * @param dn 424 * the String we are analyzing. 425 * @return <CODE>true</CODE> if the the provided string is a configuration DN 426 * and <CODE>false</CODE> otherwise. 427 */ 428 public static boolean isConfigurationDn(String dn) 429 { 430 boolean isConfigurationDn = false; 431 String[] configDns = { "cn=config", Constants.SCHEMA_DN }; 432 for (int i = 0; i < configDns.length && !isConfigurationDn; i++) 433 { 434 isConfigurationDn = areDnsEqual(dn, configDns[i]); 435 } 436 return isConfigurationDn; 437 } 438 439 /** 440 * Returns <CODE>true</CODE> if the the provided strings represent the same DN 441 * and <CODE>false</CODE> otherwise. 442 * 443 * @param dn1 444 * the first dn to compare. 445 * @param dn2 446 * the second dn to compare. 447 * @return <CODE>true</CODE> if the the provided strings represent the same DN 448 * and <CODE>false</CODE> otherwise. 449 */ 450 public static boolean areDnsEqual(String dn1, String dn2) 451 { 452 boolean areDnsEqual = false; 453 try 454 { 455 LdapName name1 = new LdapName(dn1); 456 LdapName name2 = new LdapName(dn2); 457 areDnsEqual = name1.equals(name2); 458 } 459 catch (Exception ex) 460 { 461 // do nothing 462 } 463 464 return areDnsEqual; 465 } 466 467 /** 468 * Creates the parent directory if it does not already exist. 469 * 470 * @param f 471 * File for which parentage will be insured 472 * @return boolean indicating whether or not the input <code>f</code> has a 473 * parent after this method is invoked. 474 */ 475 public static boolean insureParentsExist(File f) 476 { 477 final File parent = f.getParentFile(); 478 final boolean b = parent.exists(); 479 if (!b) 480 { 481 return parent.mkdirs(); 482 } 483 return b; 484 } 485 486 /** 487 * Creates the a directory in the provided path. 488 * 489 * @param path 490 * the path. 491 * @return <CODE>true</CODE> if the path was created or already existed (and 492 * was a directory) and <CODE>false</CODE> otherwise. 493 * @throws IOException 494 * if something goes wrong. 495 */ 496 public static boolean createDirectory(String path) throws IOException 497 { 498 return createDirectory(new File(path)); 499 } 500 501 /** 502 * Creates the a directory in the provided path. 503 * 504 * @param f 505 * the path. 506 * @return <CODE>true</CODE> if the path was created or already existed (and 507 * was a directory) and <CODE>false</CODE> otherwise. 508 * @throws IOException 509 * if something goes wrong. 510 */ 511 public static boolean createDirectory(File f) throws IOException 512 { 513 boolean directoryCreated; 514 if (!f.exists()) 515 { 516 directoryCreated = f.mkdirs(); 517 } 518 else 519 { 520 directoryCreated = f.isDirectory(); 521 } 522 return directoryCreated; 523 } 524 525 /** 526 * Creates a file on the specified path with the contents of the provided 527 * stream. 528 * 529 * @param path 530 * the path where the file will be created. 531 * @param is 532 * the InputStream with the contents of the file. 533 * @throws IOException 534 * if something goes wrong. 535 */ 536 public static void createFile(File path, InputStream is) throws IOException 537 { 538 FileOutputStream out; 539 BufferedOutputStream dest; 540 byte[] data = new byte[BUFFER_SIZE]; 541 int count; 542 543 out = new FileOutputStream(path); 544 545 dest = new BufferedOutputStream(out); 546 547 while ((count = is.read(data, 0, BUFFER_SIZE)) != -1) 548 { 549 dest.write(data, 0, count); 550 } 551 dest.flush(); 552 dest.close(); 553 } 554 555 /** 556 * Creates a file on the specified path with the contents of the provided 557 * String. The file is protected, so that 'others' have no access to it. 558 * 559 * @param path 560 * the path where the file will be created. 561 * @param content 562 * the String with the contents of the file. 563 * @throws IOException 564 * if something goes wrong. 565 * @throws InterruptedException 566 * if there is a problem changing the permissions of the file. 567 */ 568 public static void createProtectedFile(String path, String content) throws IOException, InterruptedException 569 { 570 FileWriter file = new FileWriter(path); 571 PrintWriter out = new PrintWriter(file); 572 573 out.println(content); 574 575 out.flush(); 576 out.close(); 577 578 if (!isWindows()) 579 { 580 setPermissionsUnix(path, "600"); 581 } 582 } 583 584 /** 585 * This is a helper method that gets a LocalizableMessage representation of 586 * the elements in the Collection of Messages. The LocalizableMessage will 587 * display the different elements separated by the separator String. 588 * 589 * @param col 590 * the collection containing the messages. 591 * @param separator 592 * the separator String to be used. 593 * @return the message representation for the collection; null if 594 * <code>col</code> is null 595 */ 596 public static LocalizableMessage getMessageFromCollection(Collection<LocalizableMessage> col, String separator) 597 { 598 if (col != null) 599 { 600 final LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); 601 for (LocalizableMessage m : col) 602 { 603 mb.append(separator).append(m); 604 } 605 return mb.toMessage(); 606 } 607 return null; 608 } 609 610 /** 611 * Returns the default server location that will be proposed to the user in 612 * the installation. 613 * 614 * @return the default server location that will be proposed to the user in 615 * the installation. 616 */ 617 public static String getDefaultServerLocation() 618 { 619 String userDir = System.getProperty("user.home"); 620 String firstLocation = userDir + File.separator + SHORT_NAME.toLowerCase(Locale.ENGLISH); 621 String serverLocation = firstLocation; 622 int i = 1; 623 while (fileExists(serverLocation) || directoryExistsAndIsNotEmpty(serverLocation)) 624 { 625 serverLocation = firstLocation + "-" + i; 626 i++; 627 } 628 return serverLocation; 629 } 630 631 /** 632 * Returns <CODE>true</CODE> if there is more disk space in the provided path 633 * than what is specified with the bytes parameter. 634 * 635 * @param directoryPath 636 * the path. 637 * @param bytes 638 * the disk space. 639 * @return <CODE>true</CODE> if there is more disk space in the provided path 640 * than what is specified with the bytes parameter. 641 */ 642 public static synchronized boolean hasEnoughSpace(String directoryPath, long bytes) 643 { 644 // TODO This does not work with quotas etc. but at least it seems that 645 // we do not write all data on disk if it fails. 646 boolean hasEnoughSpace = false; 647 File file = null; 648 RandomAccessFile raf = null; 649 File directory = new File(directoryPath); 650 boolean deleteDirectory = false; 651 if (!directory.exists()) 652 { 653 deleteDirectory = directory.mkdir(); 654 } 655 656 try 657 { 658 file = File.createTempFile("temp" + System.nanoTime(), ".tmp", directory); 659 raf = new RandomAccessFile(file, "rw"); 660 raf.setLength(bytes); 661 hasEnoughSpace = true; 662 } 663 catch (IOException ex) 664 { /* do nothing */ 665 } 666 finally 667 { 668 StaticUtils.close(raf); 669 if (file != null) 670 { 671 file.delete(); 672 } 673 } 674 675 if (deleteDirectory) 676 { 677 directory.delete(); 678 } 679 680 return hasEnoughSpace; 681 } 682 683 /** 684 * Gets a localized representation of the provide TopologyCacheException. 685 * 686 * @param te 687 * the exception. 688 * @return a localized representation of the provide TopologyCacheException. 689 */ 690 public static LocalizableMessage getMessage(TopologyCacheException te) 691 { 692 LocalizableMessageBuilder buf = new LocalizableMessageBuilder(); 693 694 String ldapUrl = te.getLdapUrl(); 695 if (ldapUrl != null) 696 { 697 String hostName = ldapUrl.substring(ldapUrl.indexOf("://") + 3); 698 buf.append(INFO_SERVER_ERROR.get(hostName)); 699 buf.append(" "); 700 } 701 if (te.getType() == TopologyCacheException.Type.TIMEOUT) 702 { 703 buf.append(INFO_ERROR_CONNECTING_TIMEOUT.get()); 704 } 705 else if (te.getCause() instanceof NamingException) 706 { 707 buf.append(getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), te.getCause())); 708 } 709 else 710 { 711 logger.warn(LocalizableMessage.raw("Unexpected error: " + te, te)); 712 // This is unexpected. 713 if (te.getCause() != null) 714 { 715 buf.append(getThrowableMsg(INFO_BUG_MSG.get(), te.getCause())); 716 } 717 else 718 { 719 buf.append(getThrowableMsg(INFO_BUG_MSG.get(), te)); 720 } 721 } 722 return buf.toMessage(); 723 } 724 725 /** 726 * Sets the permissions of the provided paths with the provided permission 727 * String. 728 * 729 * @param paths 730 * the paths to set permissions on. 731 * @param permissions 732 * the UNIX-mode file system permission representation (for example 733 * "644" or "755") 734 * @return the return code of the chmod command. 735 * @throws IOException 736 * if something goes wrong. 737 * @throws InterruptedException 738 * if the Runtime.exec method is interrupted. 739 */ 740 public static int setPermissionsUnix(ArrayList<String> paths, String permissions) throws IOException, 741 InterruptedException 742 { 743 String[] args = new String[paths.size() + 2]; 744 args[0] = "chmod"; 745 args[1] = permissions; 746 for (int i = 2; i < args.length; i++) 747 { 748 args[i] = paths.get(i - 2); 749 } 750 Process p = Runtime.getRuntime().exec(args); 751 return p.waitFor(); 752 } 753 754 /** 755 * Sets the permissions of the provided paths with the provided permission 756 * String. 757 * 758 * @param path 759 * to set permissions on. 760 * @param permissions 761 * the UNIX-mode file system permission representation (for example 762 * "644" or "755") 763 * @return the return code of the chmod command. 764 * @throws IOException 765 * if something goes wrong. 766 * @throws InterruptedException 767 * if the Runtime.exec method is interrupted. 768 */ 769 public static int setPermissionsUnix(String path, String permissions) throws IOException, InterruptedException 770 { 771 String[] args = new String[3]; 772 args[0] = "chmod"; 773 args[1] = permissions; 774 args[2] = path; 775 Process p = Runtime.getRuntime().exec(args); 776 return p.waitFor(); 777 } 778 779 /** 780 * Returns <CODE>true</CODE> if this is executed from command line and 781 * <CODE>false</CODE> otherwise. 782 * 783 * @return <CODE>true</CODE> if this is executed from command line and 784 * <CODE>false</CODE> otherwise. 785 */ 786 public static boolean isCli() 787 { 788 return "true".equals(System.getProperty(Constants.CLI_JAVA_PROPERTY)); 789 } 790 791 /** 792 * Creates an LDAP+StartTLS connection and returns the corresponding 793 * LdapContext. This method first creates an LdapContext with anonymous bind. 794 * Then it requests a StartTlsRequest extended operation. The StartTlsResponse 795 * is setup with the specified hostname verifier. Negotiation is done using a 796 * TrustSocketFactory so that the specified TrustManager gets called during 797 * the SSL handshake. If trust manager is null, certificates are not checked 798 * during SSL handshake. 799 * 800 * @param ldapsURL 801 * the target *LDAPS* URL. 802 * @param dn 803 * passed as Context.SECURITY_PRINCIPAL if not null. 804 * @param pwd 805 * passed as Context.SECURITY_CREDENTIALS if not null. 806 * @param timeout 807 * passed as com.sun.jndi.ldap.connect.timeout if > 0. 808 * @param env 809 * null or additional environment properties. 810 * @param trustManager 811 * null or the trust manager to be invoked during SSL. negociation. 812 * @param verifier 813 * null or the hostname verifier to be setup in the StartTlsResponse. 814 * @return the established connection with the given parameters. 815 * @throws NamingException 816 * the exception thrown when instantiating InitialLdapContext. 817 * @see javax.naming.Context 818 * @see javax.naming.ldap.InitialLdapContext 819 * @see javax.naming.ldap.StartTlsRequest 820 * @see javax.naming.ldap.StartTlsResponse 821 * @see org.opends.admin.ads.util.TrustedSocketFactory 822 */ 823 824 public static InitialLdapContext createStartTLSContext(String ldapsURL, String dn, String pwd, int timeout, 825 Hashtable<String, String> env, TrustManager trustManager, HostnameVerifier verifier) throws NamingException 826 { 827 return ConnectionUtils.createStartTLSContext(ldapsURL, dn, pwd, timeout, env, trustManager, null, verifier); 828 } 829 830 /** 831 * Returns a message object for the given NamingException. The code assume 832 * that we are trying to connect to the local server. 833 * 834 * @param ne 835 * the NamingException. 836 * @return a message object for the given NamingException. 837 */ 838 public static LocalizableMessage getMessageForException(NamingException ne) 839 { 840 final String detailedException = ne.toString(true); 841 if (isCertificateException(ne)) 842 { 843 return INFO_ERROR_READING_CONFIG_LDAP_CERTIFICATE.get(detailedException); 844 } 845 else if (ne instanceof AuthenticationException) 846 { 847 return ERR_CANNOT_CONNECT_TO_LOCAL_AUTHENTICATION.get(detailedException); 848 } 849 else if (ne instanceof NoPermissionException) 850 { 851 return ERR_CANNOT_CONNECT_TO_LOCAL_PERMISSIONS.get(detailedException); 852 } 853 else if (ne instanceof NamingSecurityException) 854 { 855 return ERR_CANNOT_CONNECT_TO_LOCAL_PERMISSIONS.get(detailedException); 856 } 857 else if (ne instanceof CommunicationException) 858 { 859 return ERR_CANNOT_CONNECT_TO_LOCAL_COMMUNICATION.get(detailedException); 860 } 861 else 862 { 863 return ERR_CANNOT_CONNECT_TO_LOCAL_GENERIC.get(detailedException); 864 } 865 } 866 867 /** 868 * Returns the path of the installation of the directory server. Note that 869 * this method assumes that this code is being run locally. 870 * 871 * @return the path of the installation of the directory server. 872 */ 873 public static String getInstallPathFromClasspath() 874 { 875 String installPath = System.getProperty("org.opends.quicksetup.Root"); 876 if (installPath != null) 877 { 878 return installPath; 879 } 880 881 /* Get the install path from the Class Path */ 882 String sep = System.getProperty("path.separator"); 883 String[] classPaths = System.getProperty("java.class.path").split(sep); 884 String path = getInstallPath(classPaths); 885 if (path != null) 886 { 887 File f = new File(path).getAbsoluteFile(); 888 File librariesDir = f.getParentFile(); 889 890 /* 891 * Do a best effort to avoid having a relative representation (for 892 * instance to avoid having ../../../). 893 */ 894 try 895 { 896 installPath = librariesDir.getParentFile().getCanonicalPath(); 897 } 898 catch (IOException ioe) 899 { 900 // Best effort 901 installPath = librariesDir.getParent(); 902 } 903 } 904 return installPath; 905 } 906 907 private static String getInstallPath(final String[] classPaths) 908 { 909 for (String classPath : classPaths) 910 { 911 final String normPath = classPath.replace(File.separatorChar, '/'); 912 if (normPath.endsWith(OPENDJ_BOOTSTRAP_CLIENT_JAR_RELATIVE_PATH) 913 || normPath.endsWith(OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH)) 914 { 915 return classPath; 916 } 917 } 918 return null; 919 } 920 921 /** 922 * Returns the path of the installation of the directory server. Note that 923 * this method assumes that this code is being run locally. 924 * 925 * @param installPath 926 * The installation path 927 * @return the path of the installation of the directory server. 928 */ 929 public static String getInstancePathFromInstallPath(String installPath) 930 { 931 String instancePathFileName = Installation.INSTANCE_LOCATION_PATH; 932 File _svcScriptPathName = new File( 933 installPath + File.separator + Installation.LIBRARIES_PATH_RELATIVE + File.separator + "_svc-opendj.sh"); 934 935 // look for /etc/opt/opendj/instance.loc 936 File f = new File(instancePathFileName); 937 if (!_svcScriptPathName.exists() || !f.exists()) 938 { 939 // look for <installPath>/instance.loc 940 instancePathFileName = installPath + File.separator + Installation.INSTANCE_LOCATION_PATH_RELATIVE; 941 f = new File(instancePathFileName); 942 if (!f.exists()) 943 { 944 return installPath; 945 } 946 } 947 948 // Read the first line and close the file. 949 try (BufferedReader reader = new BufferedReader(new FileReader(instancePathFileName))) 950 { 951 String line = reader.readLine(); 952 File instanceLoc = new File(line.trim()); 953 if (instanceLoc.isAbsolute()) 954 { 955 return getCanonicalPath(instanceLoc); 956 } 957 else 958 { 959 return getCanonicalPath(new File(installPath + File.separator + instanceLoc.getPath())); 960 } 961 } 962 catch (Exception e) 963 { 964 return installPath; 965 } 966 } 967 968 /** 969 * Returns the max size in character of a line to be displayed in the command 970 * line. 971 * 972 * @return the max size in character of a line to be displayed in the command 973 * line. 974 */ 975 public static int getCommandLineMaxLineWidth() 976 { 977 return MAX_LINE_WIDTH; 978 } 979 980 /** 981 * Puts Swing menus in the Mac OS menu bar, if using the Aqua look and feel, 982 * and sets the application name that is displayed in the application menu and 983 * in the dock. 984 * 985 * @param appName 986 * application name to display in the menu bar and the dock. 987 */ 988 public static void setMacOSXMenuBar(LocalizableMessage appName) 989 { 990 System.setProperty("apple.laf.useScreenMenuBar", "true"); 991 System.setProperty("com.apple.mrj.application.apple.menu.about.name", String.valueOf(appName)); 992 } 993 994 /** 995 * Returns the number of entries contained in the zip file. This is used to 996 * update properly the progress bar ratio. 997 * 998 * @return the number of entries contained in the zip file. 999 */ 1000 public static int getNumberZipEntries() 1001 { 1002 // TODO we should get this dynamically during build 1003 return 165; 1004 } 1005 1006 /** 1007 * Creates a string consisting of the string representation of the elements in 1008 * the <code>list</code> separated by <code>separator</code>. 1009 * 1010 * @param list 1011 * the list to print 1012 * @param separator 1013 * to use in separating elements 1014 * @param prefix 1015 * prepended to each individual element in the list before adding to 1016 * the returned string. 1017 * @param suffix 1018 * appended to each individual element in the list before adding to 1019 * the returned string. 1020 * @return String representing the list 1021 */ 1022 public static String listToString(List<?> list, String separator, String prefix, String suffix) 1023 { 1024 StringBuilder sb = new StringBuilder(); 1025 for (int i = 0; i < list.size(); i++) 1026 { 1027 if (prefix != null) 1028 { 1029 sb.append(prefix); 1030 } 1031 sb.append(list.get(i)); 1032 if (suffix != null) 1033 { 1034 sb.append(suffix); 1035 } 1036 if (i < list.size() - 1) 1037 { 1038 sb.append(separator); 1039 } 1040 } 1041 return sb.toString(); 1042 } 1043 1044 /** 1045 * Returns the file system permissions for a file. 1046 * 1047 * @param file 1048 * the file for which we want the file permissions. 1049 * @return the file system permissions for the file. 1050 */ 1051 public static String getFileSystemPermissions(File file) 1052 { 1053 String name = file.getName(); 1054 if (file.getParent().endsWith(File.separator + Installation.WINDOWS_BINARIES_PATH_RELATIVE) 1055 || file.getParent().endsWith(File.separator + Installation.UNIX_BINARIES_PATH_RELATIVE)) 1056 { 1057 return name.endsWith(".bat") ? "644" : "755"; 1058 } 1059 else if (name.endsWith(".sh") 1060 || name.endsWith(Installation.UNIX_SETUP_FILE_NAME) 1061 || name.endsWith(Installation.UNIX_UNINSTALL_FILE_NAME) 1062 || name.endsWith(Installation.UNIX_UPGRADE_FILE_NAME) 1063 || name.endsWith(Installation.MAC_JAVA_APP_STUB_NAME)) 1064 { 1065 return "755"; 1066 } 1067 else 1068 { 1069 return "644"; 1070 } 1071 } 1072 1073 /** 1074 * Inserts HTML break tags into <code>d</code> breaking it up so that ideally 1075 * no line is longer than <code>maxll</code> assuming no single word is longer 1076 * then <code>maxll</code>. If the string already contains HTML tags that 1077 * cause a line break (e.g break and closing list item tags) they are 1078 * respected by this method when calculating where to place new breaks to 1079 * control the maximum line length. 1080 * 1081 * @param cs 1082 * String to break 1083 * @param maxll 1084 * int maximum line length 1085 * @return String representing <code>d</code> with HTML break tags inserted 1086 */ 1087 public static String breakHtmlString(CharSequence cs, int maxll) 1088 { 1089 if (cs != null) 1090 { 1091 String d = cs.toString(); 1092 int len = d.length(); 1093 if (len <= 0) 1094 { 1095 return d; 1096 } 1097 if (len > maxll) 1098 { 1099 1100 // First see if there are any tags that would cause a 1101 // natural break in the line. If so start line break 1102 // point evaluation from that point. 1103 for (String tag : Constants.BREAKING_TAGS) 1104 { 1105 int p = d.lastIndexOf(tag, maxll); 1106 if (p > 0 && p < len) 1107 { 1108 return d.substring(0, p + tag.length()) + breakHtmlString(d.substring(p + tag.length()), maxll); 1109 } 1110 } 1111 1112 // Now look for spaces in which to insert a break. 1113 // First see if there are any spaces counting backward 1114 // from the max line length. If there aren't any, then 1115 // use the first space encountered after the max line 1116 // length. 1117 int p = d.lastIndexOf(' ', maxll); 1118 if (p <= 0) 1119 { 1120 p = d.indexOf(' ', maxll); 1121 } 1122 if (p > 0 && p < len) 1123 { 1124 return d.substring(0, p) + Constants.HTML_LINE_BREAK + breakHtmlString(d.substring(p + 1), maxll); 1125 } 1126 else 1127 { 1128 return d; 1129 } 1130 } 1131 else 1132 { 1133 return d; 1134 } 1135 } 1136 else 1137 { 1138 return null; 1139 } 1140 } 1141 1142 /** 1143 * Converts existing HTML break tags to native line separators. 1144 * 1145 * @param s 1146 * string to convert 1147 * @return converted string 1148 */ 1149 public static String convertHtmlBreakToLineSeparator(String s) 1150 { 1151 return s.replaceAll("<br>", Constants.LINE_SEPARATOR); 1152 } 1153 1154 /** 1155 * Strips any potential HTML markup from a given string. 1156 * 1157 * @param s 1158 * string to strip 1159 * @return resulting string 1160 */ 1161 public static String stripHtml(String s) 1162 { 1163 if (s != null) 1164 { 1165 1166 // This is not a comprehensive solution but addresses the few tags 1167 // that we have in Resources.properties at the moment. 1168 // Note that the following might strip out more than is intended for non-tags 1169 // like '<your name here>' or for funky tags like '<tag attr="1 > 0">'. 1170 // See test class for cases that might cause problems. 1171 return s.replaceAll("<.*?>", ""); 1172 } 1173 return null; 1174 } 1175 1176 /** 1177 * Tests a text string to see if it contains HTML. 1178 * 1179 * @param text 1180 * String to test 1181 * @return true if the string contains HTML 1182 */ 1183 public static boolean containsHtml(String text) 1184 { 1185 return text != null && text.indexOf('<') != -1 && text.indexOf('>') != -1; 1186 } 1187 1188 private static EmptyPrintStream emptyStream = new EmptyPrintStream(); 1189 1190 /** 1191 * Returns a printstream that does not write anything to standard output. 1192 * 1193 * @return a printstream that does not write anything to standard output. 1194 */ 1195 public static EmptyPrintStream getEmptyPrintStream() 1196 { 1197 if (emptyStream == null) 1198 { 1199 emptyStream = new EmptyPrintStream(); 1200 } 1201 return emptyStream; 1202 } 1203 1204 /** 1205 * Returns the current time of a server in milliseconds. 1206 * 1207 * @param ctx 1208 * the connection to the server. 1209 * @return the current time of a server in milliseconds. 1210 */ 1211 public static long getServerClock(InitialLdapContext ctx) 1212 { 1213 long time = -1; 1214 SearchControls ctls = new SearchControls(); 1215 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 1216 ctls.setReturningAttributes(new String[] { "currentTime" }); 1217 String filter = "(objectclass=*)"; 1218 1219 try 1220 { 1221 LdapName jndiName = new LdapName("cn=monitor"); 1222 NamingEnumeration<?> listeners = ctx.search(jndiName, filter, ctls); 1223 1224 try 1225 { 1226 while (listeners.hasMore()) 1227 { 1228 SearchResult sr = (SearchResult) listeners.next(); 1229 1230 String v = getFirstValue(sr, "currentTime"); 1231 1232 TimeZone utcTimeZone = TimeZone.getTimeZone("UTC"); 1233 1234 SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); 1235 formatter.setTimeZone(utcTimeZone); 1236 1237 time = formatter.parse(v).getTime(); 1238 } 1239 } 1240 finally 1241 { 1242 listeners.close(); 1243 } 1244 } 1245 catch (Throwable t) 1246 { 1247 logger.warn(LocalizableMessage.raw("Error retrieving server current time: " + t, t)); 1248 } 1249 return time; 1250 } 1251 1252 /** 1253 * Checks that the java version we are running is compatible with OpenDS. 1254 * 1255 * @throws IncompatibleVersionException 1256 * if the java version we are running is not compatible with OpenDS. 1257 */ 1258 public static void checkJavaVersion() throws IncompatibleVersionException 1259 { 1260 try 1261 { 1262 com.forgerock.opendj.cli.Utils.checkJavaVersion(); 1263 } 1264 catch (ClientException e) 1265 { 1266 throw new IncompatibleVersionException(e.getMessageObject(), e); 1267 } 1268 } 1269 1270 /** 1271 * Basic method to know if the host is local or not. This is only used to know 1272 * if we can perform a port check or not. 1273 * 1274 * @param host 1275 * the host to analyze. 1276 * @return <CODE>true</CODE> if it is the local host and <CODE>false</CODE> 1277 * otherwise. 1278 */ 1279 public static boolean isLocalHost(String host) 1280 { 1281 if ("localhost".equalsIgnoreCase(host)) 1282 { 1283 return true; 1284 } 1285 1286 try 1287 { 1288 InetAddress localAddress = InetAddress.getLocalHost(); 1289 InetAddress[] addresses = InetAddress.getAllByName(host); 1290 for (InetAddress address : addresses) 1291 { 1292 if (localAddress.equals(address)) 1293 { 1294 return true; 1295 } 1296 } 1297 } 1298 catch (Throwable t) 1299 { 1300 logger.warn(LocalizableMessage.raw("Failing checking host names: " + t, t)); 1301 } 1302 return false; 1303 } 1304 1305 /** 1306 * Returns the HTML representation of a plain text string which is obtained 1307 * by converting some special characters (like '<') into its equivalent 1308 * escaped HTML representation. 1309 * 1310 * @param rawString the String from which we want to obtain the HTML 1311 * representation. 1312 * @return the HTML representation of the plain text string. 1313 */ 1314 private static String escapeHtml(String rawString) 1315 { 1316 StringBuilder buffer = new StringBuilder(); 1317 for (int i = 0; i < rawString.length(); i++) 1318 { 1319 char c = rawString.charAt(i); 1320 switch (c) 1321 { 1322 case '<': 1323 buffer.append("<"); 1324 break; 1325 1326 case '>': 1327 buffer.append(">"); 1328 break; 1329 1330 case '&': 1331 buffer.append("&"); 1332 break; 1333 1334 case '"': 1335 buffer.append("""); 1336 break; 1337 1338 default: 1339 buffer.append(c); 1340 break; 1341 } 1342 } 1343 1344 return buffer.toString(); 1345 } 1346 1347 /** 1348 * Returns the HTML representation for a given text. without adding any kind 1349 * of font or style elements. Just escapes the problematic characters 1350 * (like '<') and transform the break lines into '\n' characters. 1351 * 1352 * @param text the source text from which we want to get the HTML 1353 * representation 1354 * @return the HTML representation for the given text. 1355 */ 1356 public static String getHtml(String text) 1357 { 1358 StringBuilder buffer = new StringBuilder(); 1359 if (text != null) 1360 { 1361 text = text.replaceAll("\r\n", "\n"); 1362 String[] lines = text.split("[\n\r\u0085\u2028\u2029]"); 1363 for (int i = 0; i < lines.length; i++) 1364 { 1365 if (i != 0) 1366 { 1367 buffer.append(Constants.HTML_LINE_BREAK); 1368 } 1369 buffer.append(escapeHtml(lines[i])); 1370 } 1371 } 1372 return buffer.toString(); 1373 } 1374 1375 /** 1376 * Tries to find a customized object in the customization class. If the 1377 * customization class does not exist or it does not contain the field as the 1378 * specified type of the object, returns the default value. 1379 * 1380 * @param <T> 1381 * the type of the customized object. 1382 * @param fieldName 1383 * the name of the field representing an object in the customization 1384 * class. 1385 * @param defaultValue 1386 * the default value. 1387 * @param valueClass 1388 * the class of the parametrized value. 1389 * @return the customized object. 1390 */ 1391 public static <T> T getCustomizedObject(String fieldName, T defaultValue, Class<T> valueClass) 1392 { 1393 try 1394 { 1395 Class<?> c = Class.forName(Utils.CUSTOMIZATION_CLASS_NAME); 1396 Object obj = c.newInstance(); 1397 1398 return valueClass.cast(c.getField(fieldName).get(obj)); 1399 } 1400 catch (Exception ex) 1401 { 1402 //do nothing. 1403 } 1404 return defaultValue; 1405 } 1406 1407 /** 1408 * Adds word break tags to the provided html string. 1409 * 1410 * @param htmlString 1411 * the string. 1412 * @param from 1413 * the first index to start the spacing from. 1414 * @param spacing 1415 * the minimal spacing between word breaks. 1416 * @return a string containing word breaks. 1417 */ 1418 public static String addWordBreaks(String htmlString, int from, int spacing) 1419 { 1420 StringBuilder sb = new StringBuilder(); 1421 boolean insideTag = false; 1422 int totalAddedChars = 0; 1423 int addedChars = 0; 1424 for (int i = 0; i < htmlString.length(); i++) 1425 { 1426 char c = htmlString.charAt(i); 1427 sb.append(c); 1428 if (c == '<') 1429 { 1430 insideTag = true; 1431 } 1432 else if (c == '>' && insideTag) 1433 { 1434 insideTag = false; 1435 } 1436 if (!insideTag && c != '>') 1437 { 1438 addedChars++; 1439 totalAddedChars++; 1440 } 1441 if (addedChars > spacing && totalAddedChars > from && !insideTag) 1442 { 1443 sb.append("<wbr>"); 1444 addedChars = 0; 1445 } 1446 } 1447 return sb.toString(); 1448 } 1449 1450 /** 1451 * Returns the localized string describing the DataOptions chosen by the user. 1452 * 1453 * @param userInstallData 1454 * the DataOptions of the user. 1455 * @return the localized string describing the DataOptions chosen by the user. 1456 */ 1457 public static String getDataDisplayString(final UserData userInstallData) 1458 { 1459 LocalizableMessage msg; 1460 1461 final DataReplicationOptions repl = userInstallData.getReplicationOptions(); 1462 final SuffixesToReplicateOptions suf = userInstallData.getSuffixesToReplicateOptions(); 1463 1464 boolean createSuffix = repl.getType() == DataReplicationOptions.Type.FIRST_IN_TOPOLOGY 1465 || repl.getType() == DataReplicationOptions.Type.STANDALONE 1466 || suf.getType() == SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY; 1467 1468 if (createSuffix) 1469 { 1470 LocalizableMessage arg2; 1471 NewSuffixOptions options = userInstallData.getNewSuffixOptions(); 1472 1473 switch (options.getType()) 1474 { 1475 case CREATE_BASE_ENTRY: 1476 arg2 = INFO_REVIEW_CREATE_BASE_ENTRY_LABEL.get(options.getBaseDns().getFirst()); 1477 break; 1478 1479 case LEAVE_DATABASE_EMPTY: 1480 arg2 = INFO_REVIEW_LEAVE_DATABASE_EMPTY_LABEL.get(); 1481 break; 1482 1483 case IMPORT_FROM_LDIF_FILE: 1484 arg2 = INFO_REVIEW_IMPORT_LDIF.get(options.getLDIFPaths().getFirst()); 1485 break; 1486 1487 case IMPORT_AUTOMATICALLY_GENERATED_DATA: 1488 arg2 = INFO_REVIEW_IMPORT_AUTOMATICALLY_GENERATED.get(options.getNumberEntries()); 1489 break; 1490 1491 default: 1492 throw new IllegalArgumentException("Unknown type: " + options.getType()); 1493 } 1494 1495 if (options.getBaseDns().isEmpty()) 1496 { 1497 msg = INFO_REVIEW_CREATE_NO_SUFFIX.get(); 1498 } 1499 else 1500 { 1501 final String backendType = userInstallData.getBackendType().getUserFriendlyName().toString(); 1502 if (options.getBaseDns().size() > 1) 1503 { 1504 msg = INFO_REVIEW_CREATE_SUFFIX.get( 1505 backendType, joinAsString(Constants.LINE_SEPARATOR, options.getBaseDns()), arg2); 1506 } 1507 else 1508 { 1509 msg = INFO_REVIEW_CREATE_SUFFIX.get(backendType, options.getBaseDns().getFirst(), arg2); 1510 } 1511 } 1512 } 1513 else 1514 { 1515 final StringBuilder buf = new StringBuilder(); 1516 for (final SuffixDescriptor suffix : suf.getSuffixes()) 1517 { 1518 if (buf.length() > 0) 1519 { 1520 buf.append(Constants.LINE_SEPARATOR); 1521 } 1522 buf.append(suffix.getDN()); 1523 } 1524 msg = INFO_REVIEW_REPLICATE_SUFFIX.get(buf); 1525 } 1526 1527 return msg.toString(); 1528 } 1529 1530 /** 1531 * Returns a localized String representation of the provided SecurityOptions 1532 * object. 1533 * 1534 * @param ops 1535 * the SecurityOptions object from which we want to obtain the String 1536 * representation. 1537 * @param html 1538 * whether the resulting String must be in HTML or not. 1539 * @return a localized String representation of the provided SecurityOptions 1540 * object. 1541 */ 1542 public static String getSecurityOptionsString(SecurityOptions ops, boolean html) 1543 { 1544 StringBuilder buf = new StringBuilder(); 1545 1546 if (ops.getCertificateType() == SecurityOptions.CertificateType.NO_CERTIFICATE) 1547 { 1548 buf.append(INFO_NO_SECURITY.get()); 1549 } 1550 else 1551 { 1552 if (ops.getEnableStartTLS()) 1553 { 1554 buf.append(INFO_ENABLE_STARTTLS.get()); 1555 } 1556 if (ops.getEnableSSL()) 1557 { 1558 if (buf.length() > 0) 1559 { 1560 if (html) 1561 { 1562 buf.append(Constants.HTML_LINE_BREAK); 1563 } 1564 else 1565 { 1566 buf.append("\n"); 1567 } 1568 } 1569 buf.append(INFO_ENABLE_SSL.get(ops.getSslPort())); 1570 } 1571 if (html) 1572 { 1573 buf.append(Constants.HTML_LINE_BREAK); 1574 } 1575 else 1576 { 1577 buf.append("\n"); 1578 } 1579 LocalizableMessage certMsg; 1580 switch (ops.getCertificateType()) 1581 { 1582 case SELF_SIGNED_CERTIFICATE: 1583 certMsg = INFO_SELF_SIGNED_CERTIFICATE.get(); 1584 break; 1585 1586 case JKS: 1587 certMsg = INFO_JKS_CERTIFICATE.get(); 1588 break; 1589 1590 case JCEKS: 1591 certMsg = INFO_JCEKS_CERTIFICATE.get(); 1592 break; 1593 1594 case PKCS11: 1595 certMsg = INFO_PKCS11_CERTIFICATE.get(); 1596 break; 1597 1598 case PKCS12: 1599 certMsg = INFO_PKCS12_CERTIFICATE.get(); 1600 break; 1601 1602 default: 1603 throw new IllegalStateException("Unknown certificate options type: " + ops.getCertificateType()); 1604 } 1605 buf.append(certMsg); 1606 } 1607 1608 if (html) 1609 { 1610 return "<html>" + UIFactory.applyFontToHtml(buf.toString(), UIFactory.SECONDARY_FIELD_VALID_FONT); 1611 } 1612 else 1613 { 1614 return buf.toString(); 1615 } 1616 } 1617 1618 /** 1619 * Returns a String representation of the provided command-line. 1620 * 1621 * @param cmd 1622 * the command-line arguments. 1623 * @param formatter 1624 * the formatted to be used to create the String representation. 1625 * @return a String representation of the provided command-line. 1626 */ 1627 public static String getFormattedEquivalentCommandLine(List<String> cmd, ProgressMessageFormatter formatter) 1628 { 1629 StringBuilder builder = new StringBuilder(); 1630 builder.append(formatter.getFormattedProgress(LocalizableMessage.raw(cmd.get(0)))); 1631 int initialIndex = 1; 1632 StringBuilder sbSeparator = new StringBuilder(); 1633 sbSeparator.append(formatter.getSpace()); 1634 if (!isWindows()) 1635 { 1636 sbSeparator.append("\\"); 1637 sbSeparator.append(formatter.getLineBreak()); 1638 for (int i = 0; i < 10; i++) 1639 { 1640 sbSeparator.append(formatter.getSpace()); 1641 } 1642 } 1643 1644 String lineSeparator = sbSeparator.toString(); 1645 for (int i = initialIndex; i < cmd.size(); i++) 1646 { 1647 String s = cmd.get(i); 1648 if (s.startsWith("-")) 1649 { 1650 builder.append(lineSeparator); 1651 builder.append(formatter.getFormattedProgress(LocalizableMessage.raw(s))); 1652 } 1653 else 1654 { 1655 builder.append(formatter.getSpace()); 1656 builder.append(formatter.getFormattedProgress(LocalizableMessage.raw(escapeCommandLineValue(s)))); 1657 } 1658 } 1659 return builder.toString(); 1660 } 1661 1662 /** 1663 * This method simply takes a value and tries to transform it (with escape or 1664 * '"') characters so that it can be used in a command line. 1665 * 1666 * @param value 1667 * the String to be treated. 1668 * @return the transformed value. 1669 */ 1670 public static String escapeCommandLineValue(String value) 1671 { 1672 StringBuilder b = new StringBuilder(); 1673 if (isUnix()) 1674 { 1675 for (int i = 0; i < value.length(); i++) 1676 { 1677 char c = value.charAt(i); 1678 boolean charToEscapeFound = false; 1679 for (int j = 0; j < CHARS_TO_ESCAPE.length && !charToEscapeFound; j++) 1680 { 1681 charToEscapeFound = c == CHARS_TO_ESCAPE[j]; 1682 } 1683 if (charToEscapeFound) 1684 { 1685 b.append('\\'); 1686 } 1687 b.append(c); 1688 } 1689 } 1690 else 1691 { 1692 b.append('"').append(value).append('"'); 1693 } 1694 1695 return b.toString(); 1696 } 1697 1698 /** 1699 * Returns the equivalent setup CLI command-line. Note that this command-line 1700 * does not cover all the replication part of the GUI install. 1701 * 1702 * @param userData 1703 * the user data. 1704 * @return the equivalent setup command-line. 1705 */ 1706 public static List<String> getSetupEquivalentCommandLine(final UserData userData) 1707 { 1708 List<String> cmdLine = new ArrayList<>(); 1709 cmdLine.add(getInstallDir(userData) + getSetupFilename()); 1710 cmdLine.add("--cli"); 1711 1712 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType = 1713 userData.getBackendType(); 1714 if (backendType != null) 1715 { 1716 cmdLine.add("--" + ArgumentConstants.OPTION_LONG_BACKEND_TYPE); 1717 cmdLine.add(BackendTypeHelper.filterSchemaBackendName(backendType.getName())); 1718 } 1719 1720 for (final String baseDN : getBaseDNs(userData)) 1721 { 1722 cmdLine.add("--baseDN"); 1723 cmdLine.add(baseDN); 1724 } 1725 1726 switch (userData.getNewSuffixOptions().getType()) 1727 { 1728 case CREATE_BASE_ENTRY: 1729 cmdLine.add("--addBaseEntry"); 1730 break; 1731 1732 case IMPORT_AUTOMATICALLY_GENERATED_DATA: 1733 cmdLine.add("--sampleData"); 1734 cmdLine.add(Integer.toString(userData.getNewSuffixOptions().getNumberEntries())); 1735 break; 1736 1737 case IMPORT_FROM_LDIF_FILE: 1738 for (final String ldifFile : userData.getNewSuffixOptions().getLDIFPaths()) 1739 { 1740 cmdLine.add("--ldifFile"); 1741 cmdLine.add(ldifFile); 1742 } 1743 1744 final String rejectFile = userData.getNewSuffixOptions().getRejectedFile(); 1745 if (rejectFile != null) 1746 { 1747 cmdLine.add("--rejectFile"); 1748 cmdLine.add(rejectFile); 1749 } 1750 1751 final String skipFile = userData.getNewSuffixOptions().getSkippedFile(); 1752 if (skipFile != null) 1753 { 1754 cmdLine.add("--skipFile"); 1755 cmdLine.add(skipFile); 1756 } 1757 break; 1758 1759 default: 1760 break; 1761 } 1762 1763 cmdLine.add("--ldapPort"); 1764 cmdLine.add(Integer.toString(userData.getServerPort())); 1765 1766 cmdLine.add("--adminConnectorPort"); 1767 cmdLine.add(Integer.toString(userData.getAdminConnectorPort())); 1768 1769 if (userData.getServerJMXPort() != -1) 1770 { 1771 cmdLine.add("--jmxPort"); 1772 cmdLine.add(Integer.toString(userData.getServerJMXPort())); 1773 } 1774 1775 cmdLine.add("--rootUserDN"); 1776 cmdLine.add(userData.getDirectoryManagerDn()); 1777 1778 cmdLine.add("--rootUserPassword"); 1779 cmdLine.add(OBFUSCATED_VALUE); 1780 1781 if (isWindows() && userData.getEnableWindowsService()) 1782 { 1783 cmdLine.add("--enableWindowsService"); 1784 } 1785 1786 if (userData.getReplicationOptions().getType() == DataReplicationOptions.Type.STANDALONE 1787 && !userData.getStartServer()) 1788 { 1789 cmdLine.add("--doNotStart"); 1790 } 1791 1792 if (userData.getSecurityOptions().getEnableStartTLS()) 1793 { 1794 cmdLine.add("--enableStartTLS"); 1795 } 1796 1797 if (userData.getSecurityOptions().getEnableSSL()) 1798 { 1799 cmdLine.add("--ldapsPort"); 1800 cmdLine.add(Integer.toString(userData.getSecurityOptions().getSslPort())); 1801 } 1802 1803 cmdLine.addAll(getSecurityOptionSetupEquivalentCmdLine(userData)); 1804 cmdLine.add("--no-prompt"); 1805 cmdLine.add("--noPropertiesFile"); 1806 1807 return cmdLine; 1808 } 1809 1810 private static String getSetupFilename() 1811 { 1812 return isWindows() ? Installation.WINDOWS_SETUP_FILE_NAME : Installation.UNIX_SETUP_FILE_NAME; 1813 } 1814 1815 private static List<String> getSecurityOptionSetupEquivalentCmdLine(final UserData userData) 1816 { 1817 final List<String> cmdLine = new ArrayList<>(); 1818 1819 switch (userData.getSecurityOptions().getCertificateType()) 1820 { 1821 case SELF_SIGNED_CERTIFICATE: 1822 cmdLine.add("--generateSelfSignedCertificate"); 1823 cmdLine.add("--hostName"); 1824 cmdLine.add(userData.getHostName()); 1825 break; 1826 1827 case JKS: 1828 cmdLine.add("--useJavaKeystore"); 1829 cmdLine.add(userData.getSecurityOptions().getKeystorePath()); 1830 addKeyStoreAndCert(userData.getSecurityOptions(), cmdLine); 1831 break; 1832 1833 case JCEKS: 1834 cmdLine.add("--useJCEKS"); 1835 cmdLine.add(userData.getSecurityOptions().getKeystorePath()); 1836 1837 addKeyStoreAndCert(userData.getSecurityOptions(), cmdLine); 1838 break; 1839 1840 case PKCS12: 1841 cmdLine.add("--usePkcs12keyStore"); 1842 cmdLine.add(userData.getSecurityOptions().getKeystorePath()); 1843 1844 addKeyStoreAndCert(userData.getSecurityOptions(), cmdLine); 1845 break; 1846 1847 case PKCS11: 1848 cmdLine.add("--usePkcs11Keystore"); 1849 1850 addKeyStoreAndCert(userData.getSecurityOptions(), cmdLine); 1851 break; 1852 1853 default: 1854 break; 1855 } 1856 1857 return cmdLine; 1858 } 1859 1860 private static void addKeyStoreAndCert(final SecurityOptions securityOptions, final List<String> cmdLine) 1861 { 1862 if (securityOptions.getKeystorePassword() != null) 1863 { 1864 cmdLine.add("--keyStorePassword"); 1865 cmdLine.add(OBFUSCATED_VALUE); 1866 } 1867 1868 for(String alias : securityOptions.getAliasesToUse()) 1869 { 1870 cmdLine.add("--certNickname"); 1871 cmdLine.add(alias); 1872 } 1873 } 1874 1875 /** 1876 * Returns the list of equivalent command-lines that must be executed to 1877 * enable or initialize replication as the setup does. 1878 * 1879 * @param subcommand 1880 * either {@code "enable"} or {@code "initialize"} 1881 * @param userData 1882 * the user data. 1883 * @return the list of equivalent command-lines that must be executed to 1884 * enable or initialize replication as the setup does. 1885 */ 1886 public static List<List<String>> getDsReplicationEquivalentCommandLines(String subcommand, UserData userData) 1887 { 1888 final List<List<String>> cmdLines = new ArrayList<>(); 1889 final Map<ServerDescriptor, Set<String>> hmServerBaseDNs = getServerDescriptorBaseDNMap(userData); 1890 for (ServerDescriptor server : hmServerBaseDNs.keySet()) 1891 { 1892 cmdLines.add(getDsReplicationEquivalentCommandLine(subcommand, userData, hmServerBaseDNs.get(server), server)); 1893 } 1894 return cmdLines; 1895 } 1896 1897 private static void addEnableCommandOptions(UserData userData, ServerDescriptor server, List<String> cmdLine) 1898 { 1899 DataReplicationOptions replOptions = userData.getReplicationOptions(); 1900 cmdLine.add("--host1"); 1901 cmdLine.add(server.getHostName()); 1902 cmdLine.add("--port1"); 1903 cmdLine.add(String.valueOf(server.getEnabledAdministrationPorts().get(0))); 1904 1905 AuthenticationData authData = userData.getReplicationOptions().getAuthenticationData(); 1906 if (!Utils.areDnsEqual(authData.getDn(), ADSContext.getAdministratorDN(userData.getGlobalAdministratorUID()))) 1907 { 1908 cmdLine.add("--bindDN1"); 1909 cmdLine.add(authData.getDn()); 1910 cmdLine.add("--bindPassword1"); 1911 cmdLine.add(OBFUSCATED_VALUE); 1912 } 1913 for (ServerDescriptor s : userData.getRemoteWithNoReplicationPort().keySet()) 1914 { 1915 if (s.getAdminConnectorURL().equals(server.getAdminConnectorURL())) 1916 { 1917 AuthenticationData remoteRepl = userData.getRemoteWithNoReplicationPort().get(server); 1918 int remoteReplicationPort = remoteRepl.getPort(); 1919 1920 cmdLine.add("--replicationPort1"); 1921 cmdLine.add(String.valueOf(remoteReplicationPort)); 1922 if (remoteRepl.useSecureConnection()) 1923 { 1924 cmdLine.add("--secureReplication1"); 1925 } 1926 } 1927 } 1928 cmdLine.add("--host2"); 1929 cmdLine.add(userData.getHostName()); 1930 cmdLine.add("--port2"); 1931 cmdLine.add(String.valueOf(userData.getAdminConnectorPort())); 1932 cmdLine.add("--bindDN2"); 1933 cmdLine.add(userData.getDirectoryManagerDn()); 1934 cmdLine.add("--bindPassword2"); 1935 cmdLine.add(OBFUSCATED_VALUE); 1936 if (replOptions.getReplicationPort() != -1) 1937 { 1938 cmdLine.add("--replicationPort2"); 1939 cmdLine.add(String.valueOf(replOptions.getReplicationPort())); 1940 if (replOptions.useSecureReplication()) 1941 { 1942 cmdLine.add("--secureReplication2"); 1943 } 1944 } 1945 } 1946 1947 /** 1948 * Returns the full path of the command-line for a given script name. 1949 * 1950 * @param userData 1951 * the user data. 1952 * @param scriptBasicName 1953 * the script basic name (with no extension). 1954 * @return the full path of the command-line for a given script name. 1955 */ 1956 private static String getCommandLinePath(UserData userData, String scriptBasicName) 1957 { 1958 String cmdLineName; 1959 if (isWindows()) 1960 { 1961 cmdLineName = 1962 getInstallDir(userData) + Installation.WINDOWS_BINARIES_PATH_RELATIVE + File.separatorChar + scriptBasicName 1963 + ".bat"; 1964 } 1965 else 1966 { 1967 cmdLineName = 1968 getInstallDir(userData) + Installation.UNIX_BINARIES_PATH_RELATIVE + File.separatorChar + scriptBasicName; 1969 } 1970 return cmdLineName; 1971 } 1972 1973 private static String installDir; 1974 1975 /** 1976 * Returns the installation directory. 1977 * 1978 * @return the installation directory. 1979 */ 1980 private static String getInstallDir(UserData userData) 1981 { 1982 if (installDir == null) 1983 { 1984 File f = org.opends.quicksetup.Installation.getLocal().getRootDirectory(); 1985 installDir = getCanonicalPath(f); 1986 if (installDir.lastIndexOf(File.separatorChar) != installDir.length() - 1) 1987 { 1988 installDir += File.separatorChar; 1989 } 1990 } 1991 1992 return installDir; 1993 } 1994 1995 private static String getCanonicalPath(File f) 1996 { 1997 try 1998 { 1999 return f.getCanonicalPath(); 2000 } 2001 catch (IOException t) 2002 { 2003 return f.getAbsolutePath(); 2004 } 2005 } 2006 2007 private static List<String> getDsReplicationEquivalentCommandLine(String subcommand, UserData userData, 2008 Set<String> baseDNs, ServerDescriptor server) 2009 { 2010 List<String> cmdLine = new ArrayList<>(); 2011 String cmdName = getCommandLinePath(userData, "dsreplication"); 2012 cmdLine.add(cmdName); 2013 cmdLine.add(subcommand); 2014 2015 if ("enable".equals(subcommand)) 2016 { 2017 addEnableCommandOptions(userData, server, cmdLine); 2018 } 2019 else if ("initialize".equals(subcommand)) 2020 { 2021 addInitializeCommandOptions(userData, server, cmdLine); 2022 } 2023 else 2024 { 2025 throw new IllegalArgumentException("Code is not implemented for subcommand " + subcommand); 2026 } 2027 2028 addCommonOptions(userData, baseDNs, cmdLine); 2029 return cmdLine; 2030 } 2031 2032 private static void addInitializeCommandOptions(UserData userData, ServerDescriptor server, List<String> cmdLine) 2033 { 2034 cmdLine.add("--hostSource"); 2035 cmdLine.add(server.getHostName()); 2036 cmdLine.add("--portSource"); 2037 cmdLine.add(String.valueOf(server.getEnabledAdministrationPorts().get(0))); 2038 2039 cmdLine.add("--hostDestination"); 2040 cmdLine.add(userData.getHostName()); 2041 cmdLine.add("--portDestination"); 2042 cmdLine.add(String.valueOf(userData.getAdminConnectorPort())); 2043 } 2044 2045 private static void addCommonOptions(UserData userData, Set<String> baseDNs, List<String> cmdLine) 2046 { 2047 for (String baseDN : baseDNs) 2048 { 2049 cmdLine.add("--baseDN"); 2050 cmdLine.add(baseDN); 2051 } 2052 2053 cmdLine.add("--adminUID"); 2054 cmdLine.add(userData.getGlobalAdministratorUID()); 2055 cmdLine.add("--adminPassword"); 2056 cmdLine.add(OBFUSCATED_VALUE); 2057 2058 cmdLine.add("--trustAll"); 2059 cmdLine.add("--no-prompt"); 2060 cmdLine.add("--noPropertiesFile"); 2061 } 2062 2063 private static List<String> getBaseDNs(UserData userData) 2064 { 2065 List<String> baseDNs = new ArrayList<>(); 2066 2067 DataReplicationOptions repl = userData.getReplicationOptions(); 2068 SuffixesToReplicateOptions suf = userData.getSuffixesToReplicateOptions(); 2069 2070 boolean createSuffix = 2071 repl.getType() == DataReplicationOptions.Type.FIRST_IN_TOPOLOGY 2072 || repl.getType() == DataReplicationOptions.Type.STANDALONE 2073 || suf.getType() == SuffixesToReplicateOptions.Type.NEW_SUFFIX_IN_TOPOLOGY; 2074 2075 if (createSuffix) 2076 { 2077 NewSuffixOptions options = userData.getNewSuffixOptions(); 2078 baseDNs.addAll(options.getBaseDns()); 2079 } 2080 else 2081 { 2082 Set<SuffixDescriptor> suffixes = suf.getSuffixes(); 2083 for (SuffixDescriptor suffix : suffixes) 2084 { 2085 baseDNs.add(suffix.getDN()); 2086 } 2087 } 2088 return baseDNs; 2089 } 2090 2091 private static Map<ServerDescriptor, Set<String>> getServerDescriptorBaseDNMap(UserData userData) 2092 { 2093 Map<ServerDescriptor, Set<String>> hm = new HashMap<>(); 2094 2095 Set<SuffixDescriptor> suffixes = userData.getSuffixesToReplicateOptions().getSuffixes(); 2096 AuthenticationData authData = userData.getReplicationOptions().getAuthenticationData(); 2097 String ldapURL = 2098 ConnectionUtils.getLDAPUrl(authData.getHostName(), authData.getPort(), authData.useSecureConnection()); 2099 for (SuffixDescriptor suffix : suffixes) 2100 { 2101 boolean found = false; 2102 for (ReplicaDescriptor replica : suffix.getReplicas()) 2103 { 2104 if (ldapURL.equalsIgnoreCase(replica.getServer().getAdminConnectorURL())) 2105 { 2106 // This is the server we're configuring 2107 found = true; 2108 Set<String> baseDNs = hm.get(replica.getServer()); 2109 if (baseDNs == null) 2110 { 2111 baseDNs = new LinkedHashSet<>(); 2112 hm.put(replica.getServer(), baseDNs); 2113 } 2114 baseDNs.add(suffix.getDN()); 2115 break; 2116 } 2117 } 2118 if (!found) 2119 { 2120 for (ReplicaDescriptor replica : suffix.getReplicas()) 2121 { 2122 if (hm.keySet().contains(replica.getServer())) 2123 { 2124 hm.get(replica.getServer()).add(suffix.getDN()); 2125 found = true; 2126 break; 2127 } 2128 } 2129 } 2130 if (!found) 2131 { 2132 // We haven't found the server yet, just take the first one 2133 ReplicaDescriptor replica = suffix.getReplicas().iterator().next(); 2134 if (replica != null) 2135 { 2136 Set<String> baseDNs = new LinkedHashSet<>(); 2137 hm.put(replica.getServer(), baseDNs); 2138 baseDNs.add(suffix.getDN()); 2139 } 2140 } 2141 } 2142 return hm; 2143 } 2144 2145 /** 2146 * Returns the equivalent dsconfig command-line required to configure the 2147 * first replicated server in the topology. 2148 * 2149 * @param userData 2150 * the user data. 2151 * @return the equivalent dsconfig command-line required to configure the 2152 * first replicated server in the topology. 2153 */ 2154 public static List<List<String>> getDsConfigReplicationEnableEquivalentCommandLines(UserData userData) 2155 { 2156 final List<List<String>> cmdLines = new ArrayList<>(); 2157 final String cmdName = getCommandLinePath(userData, "dsconfig"); 2158 2159 List<String> connectionArgs = new ArrayList<>(); 2160 connectionArgs.add("--hostName"); 2161 connectionArgs.add(userData.getHostName()); 2162 connectionArgs.add("--port"); 2163 connectionArgs.add(String.valueOf(userData.getAdminConnectorPort())); 2164 connectionArgs.add("--bindDN"); 2165 connectionArgs.add(userData.getDirectoryManagerDn()); 2166 connectionArgs.add("--bindPassword"); 2167 connectionArgs.add(OBFUSCATED_VALUE); 2168 connectionArgs.add("--trustAll"); 2169 connectionArgs.add("--no-prompt"); 2170 connectionArgs.add("--noPropertiesFile"); 2171 2172 List<String> cmdReplicationServer = new ArrayList<>(); 2173 cmdReplicationServer.add(cmdName); 2174 cmdReplicationServer.add("create-replication-server"); 2175 cmdReplicationServer.add("--provider-name"); 2176 cmdReplicationServer.add("Multimaster Synchronization"); 2177 cmdReplicationServer.add("--set"); 2178 cmdReplicationServer.add("replication-port:" + userData.getReplicationOptions().getReplicationPort()); 2179 cmdReplicationServer.add("--set"); 2180 cmdReplicationServer.add("replication-server-id:1"); 2181 cmdReplicationServer.add("--type"); 2182 cmdReplicationServer.add("generic"); 2183 cmdReplicationServer.addAll(connectionArgs); 2184 2185 cmdLines.add(cmdReplicationServer); 2186 return cmdLines; 2187 } 2188} 2189 2190/** 2191 * This class is used to avoid displaying the error message related to display 2192 * problems that we might have when trying to display the SplashWindow. 2193 */ 2194class EmptyPrintStream extends PrintStream 2195{ 2196 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 2197 2198 /** Default constructor. */ 2199 public EmptyPrintStream() 2200 { 2201 super(new ByteArrayOutputStream(), true); 2202 } 2203 2204 @Override 2205 public void println(String msg) 2206 { 2207 logger.info(LocalizableMessage.raw("EmptyStream msg: " + msg)); 2208 } 2209}