1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package com.sun.star.lib.loader; 25 26 import java.io.BufferedReader; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.InputStream; 30 import java.io.InputStreamReader; 31 import java.io.IOException; 32 import java.io.UnsupportedEncodingException; 33 import java.net.MalformedURLException; 34 import java.net.URL; 35 import java.util.StringTokenizer; 36 import java.util.Vector; 37 38 /** 39 * This class finds a UNO installation on the system. 40 * 41 * <p>A UNO installation can be specified by the user by either setting the 42 * com.sun.star.lib.loader.unopath system property or by setting the 43 * UNO_PATH environment variable to the program directory of a UNO 44 * installation. 45 * Note, that Java 1.3.1 and Java 1.4 don't support environment variables 46 * (System.getenv() throws java.lang.Error) and therefore setting the UNO_PATH 47 * environment variable won't work with those Java versions. 48 * If no UNO installation is specified by the user, the default installation 49 * on the system will be returned.</p> 50 * 51 * <p>On the Windows platform the default installation is read from the Windows 52 * Registry.</p> 53 * 54 * <p>On the Unix/Linux platforms the default installation is found from the 55 * PATH environment variable. Note, that for Java 1.3.1 and Java 1.4 the 56 * default installation is found by using the 'which' command, because 57 * environment variables are not supported with those Java versions. 58 * Both methods require that the 'soffice' executable or a symbolic 59 * link is in one of the directories listed in the PATH environment variable. 60 * For older versions than OOo 2.0 the above described methods may fail. 61 * In this case the default installation is taken from the .sversionrc file in 62 * the user's home directory. Note, that the .sversionrc file will be omitted 63 * for OOo 2.0</p> 64 */ 65 final class InstallationFinder { 66 67 private static final String SYSPROP_NAME = 68 "com.sun.star.lib.loader.unopath"; 69 private static final String ENVVAR_NAME = "UNO_PATH"; 70 private static final String SOFFICE = "soffice"; // Unix/Linux only 71 InstallationFinder()72 private InstallationFinder() {} // do not instantiate 73 74 /** 75 * Gets the path of a UNO installation. 76 * 77 * @return the installation path or <code>null</code>, if no installation 78 * was specified or found, or if an error occurred 79 */ getPath()80 public static String getPath() { 81 82 String path = null; 83 84 // get the installation path from the Java system property 85 // com.sun.star.lib.loader.unopath 86 // (all platforms) 87 path = getPathFromProperty( SYSPROP_NAME ); 88 if ( path == null ) { 89 // get the installation path from the UNO_PATH environment variable 90 // (all platforms, not working for Java 1.3.1 and Java 1.4) 91 path = getPathFromEnvVar( ENVVAR_NAME ); 92 if ( path == null ) { 93 String osname = null; 94 try { 95 osname = System.getProperty( "os.name" ); 96 } catch ( SecurityException e ) { 97 // if a SecurityException was thrown, 98 // return <code>null</code> 99 return null; 100 } 101 if ( osname != null ) { 102 if ( osname.startsWith( "Windows" ) ) { 103 // get the installation path from the Windows Registry 104 // (Windows platform only) 105 path = getPathFromWindowsRegistry(); 106 } else { 107 // get the installation path from the PATH environment 108 // variable (Unix/Linux platforms only, not working for 109 // Java 1.3.1 and Java 1.4) 110 path = getPathFromPathEnvVar(); 111 if ( path == null ) { 112 // get the installation path from the 'which' 113 // command (Unix/Linux platforms only) 114 path = getPathFromWhich(); 115 if ( path == null ) { 116 // get the installation path from the 117 // .sversionrc file (Unix/Linux platforms only, 118 // for older versions than OOo 2.0) 119 path = getPathFromSVersionFile(); 120 } 121 } 122 } 123 } 124 } 125 } 126 127 return path; 128 } 129 130 /** 131 * Gets the installation path from a Java system property. 132 * 133 * <p>This method is called on all platforms. 134 * The Java system property can be passed into the application by using 135 * the -D flag, e.g. 136 * java -D<property name>=<installation path> -jar application.jar.</p> 137 * 138 * @return the installation path or <code>null</code>, if no installation 139 * was specified in the Java system property or if an error occurred 140 */ getPathFromProperty( String prop )141 private static String getPathFromProperty( String prop ) { 142 143 String path = null; 144 145 try { 146 path = System.getProperty( prop ); 147 } catch ( SecurityException e ) { 148 // if a SecurityException was thrown, return <code>null</code> 149 } 150 151 return path; 152 } 153 154 /** 155 * Gets the installation path from an environment variable. 156 * 157 * <p>This method is called on all platforms. 158 * Note, that in Java 1.3.1 and Java 1.4 System.getenv() throws 159 * java.lang.Error and therefore this method returns null for those 160 * Java versions.</p> 161 * 162 * @return the installation path or <code>null</code>, if no installation 163 * was specified in the environment variable or if an error occurred 164 */ getPathFromEnvVar( String var )165 private static String getPathFromEnvVar( String var ) { 166 167 String path = null; 168 169 try { 170 path = System.getenv( var ); 171 } catch ( SecurityException e ) { 172 // if a SecurityException was thrown, return <code>null</code> 173 } catch ( java.lang.Error err ) { 174 // System.getenv() throws java.lang.Error in Java 1.3.1 and 175 // Java 1.4 176 } 177 178 return path; 179 } 180 181 /** 182 * Gets the installation path from the Windows Registry. 183 * 184 * <p>This method is called on the Windows platform only.</p> 185 * 186 * @return the installation path or <code>null</code>, if no installation 187 * was found or if an error occurred 188 */ getPathFromWindowsRegistry()189 private static String getPathFromWindowsRegistry() { 190 191 final String SUBKEYNAME = "Software\\OpenOffice\\UNO\\InstallPath"; 192 final String SUBKEYNAME64 = "Software\\Wow6432Node\\OpenOffice\\UNO\\InstallPath"; 193 194 String path = null; 195 196 try { 197 // read the key's default value from HKEY_CURRENT_USER 198 WinRegKey key = new WinRegKey( "HKEY_CURRENT_USER", SUBKEYNAME ); 199 path = key.getStringValue( "" ); // default 200 } catch ( WinRegKeyException e ) { 201 try { 202 // read the key's default value from HKEY_LOCAL_MACHINE 203 WinRegKey key = new WinRegKey( "HKEY_CURRENT_USER", 204 SUBKEYNAME64 ); 205 path = key.getStringValue( "" ); // default 206 } catch ( WinRegKeyException e64 ) { 207 try { 208 // read the key's default value from HKEY_LOCAL_MACHINE 209 WinRegKey key = new WinRegKey( "HKEY_LOCAL_MACHINE", 210 SUBKEYNAME ); 211 path = key.getStringValue( "" ); // default 212 } catch ( WinRegKeyException we ) { 213 try { 214 // read the key's default value from HKEY_LOCAL_MACHINE 215 WinRegKey key = new WinRegKey( "HKEY_LOCAL_MACHINE", 216 SUBKEYNAME64 ); 217 path = key.getStringValue( "" ); // default 218 } catch ( WinRegKeyException we64 ) { 219 System.err.println( "com.sun.star.lib.loader." + 220 "InstallationFinder::getPathFromWindowsRegistry: " + 221 "reading key from Windows Registry failed: " + we64 ); 222 } 223 } 224 } 225 } 226 227 return path; 228 } 229 230 /** 231 * Gets the installation path from the PATH environment variable. 232 * 233 * <p>This method is called on Unix/Linux platforms only. 234 * An installation is found, if the executable 'soffice' or a symbolic link 235 * is in one of the directories listed in the PATH environment variable. 236 * Note, that in Java 1.3.1 and Java 1.4 System.getenv() throws 237 * java.lang.Error and therefore this method returns null for those 238 * Java versions.</p> 239 * 240 * @return the installation path or <code>null</code>, if no installation 241 * was found or if an error occurred 242 */ getPathFromPathEnvVar()243 private static String getPathFromPathEnvVar() { 244 245 final String PATH_ENVVAR_NAME = "PATH"; 246 247 String path = null; 248 String str = null; 249 250 try { 251 str = System.getenv( PATH_ENVVAR_NAME ); 252 } catch ( SecurityException e ) { 253 // if a SecurityException was thrown, return <code>null</code> 254 return null; 255 } catch ( java.lang.Error err ) { 256 // System.getenv() throws java.lang.Error in Java 1.3.1 and 257 // Java 1.4 258 return null; 259 } 260 261 if ( str != null ) { 262 StringTokenizer tokens = new StringTokenizer( 263 str, File.pathSeparator ); 264 while ( tokens.hasMoreTokens() ) { 265 File file = new File( tokens.nextToken(), SOFFICE ); 266 try { 267 if ( file.exists() ) { 268 try { 269 // resolve symlink 270 path = file.getCanonicalFile().getParent(); 271 if ( path != null ) 272 break; 273 } catch ( IOException e ) { 274 // if an I/O exception is thrown, ignore this 275 // path entry and try the next one 276 System.err.println( "com.sun.star.lib.loader." + 277 "InstallationFinder::getPathFromEnvVar: " + 278 "bad path: " + e ); 279 } 280 } 281 } catch ( SecurityException e ) { 282 // if a SecurityException was thrown, ignore this path 283 // entry and try the next one 284 } 285 } 286 } 287 288 return path; 289 } 290 291 /** 292 * Gets the installation path from the 'which' command on Unix/Linux 293 * platforms. 294 * 295 * <p>This method is called on Unix/Linux platforms only. 296 * An installation is found, if the executable 'soffice' or a symbolic link 297 * is in one of the directories listed in the PATH environment variable.</p> 298 * 299 * @return the installation path or <code>null</code>, if no installation 300 * was found or if an error occurred 301 */ getPathFromWhich()302 private static String getPathFromWhich() { 303 304 final String WHICH = "which"; 305 306 String path = null; 307 308 // start the which process 309 String[] cmdArray = new String[2]; 310 cmdArray[0] = WHICH; 311 cmdArray[1] = SOFFICE; 312 Process proc = null; 313 Runtime rt = Runtime.getRuntime(); 314 try { 315 proc = rt.exec( cmdArray ); 316 } catch ( SecurityException e ) { 317 return null; 318 } catch ( IOException e ) { 319 // if an I/O exception is thrown, return <code>null</null> 320 System.err.println( "com.sun.star.lib.loader." + 321 "InstallationFinder::getPathFromWhich: " + 322 "which command failed: " + e ); 323 return null; 324 } 325 326 // empty standard error stream in a separate thread 327 StreamGobbler gobbler = new StreamGobbler( proc.getErrorStream() ); 328 gobbler.start(); 329 330 // read the which output from standard input stream 331 BufferedReader br = new BufferedReader( 332 new InputStreamReader( proc.getInputStream() ) ); 333 String line = null; 334 try { 335 while ( ( line = br.readLine() ) != null ) { 336 if ( path == null ) { 337 // get the path from the which output 338 int index = line.lastIndexOf( SOFFICE ); 339 if ( index != -1 ) { 340 int end = index + SOFFICE.length(); 341 for ( int i = 0; i <= index; i++ ) { 342 File file = new File( line.substring( i, end ) ); 343 try { 344 if ( file.exists() ) { 345 // resolve symlink 346 path = file.getCanonicalFile().getParent(); 347 if ( path != null ) 348 break; 349 } 350 } catch ( SecurityException e ) { 351 return null; 352 } 353 } 354 } 355 } 356 } 357 } catch ( IOException e ) { 358 // if an I/O exception is thrown, return <code>null</null> 359 System.err.println( "com.sun.star.lib.loader." + 360 "InstallationFinder::getPathFromWhich: " + 361 "reading which command output failed: " + e ); 362 return null; 363 } finally { 364 if ( br != null ) { 365 try { 366 br.close(); 367 } catch ( IOException e ) { 368 // closing standard input stream failed, ignore 369 } 370 } 371 } 372 373 try { 374 // wait until the which process has terminated 375 proc.waitFor(); 376 } catch ( InterruptedException e ) { 377 // the current thread was interrupted by another thread, 378 // kill the which process 379 proc.destroy(); 380 // set the interrupted status 381 Thread.currentThread().interrupt(); 382 } 383 384 return path; 385 } 386 387 /** 388 * Gets the installation path from the .sversionrc file in the user's home 389 * directory. 390 * 391 * <p>This method is called on Unix/Linux platforms only. 392 * The .sversionrc file is written during setup and will be omitted for 393 * OOo 2.0.</p> 394 * 395 * @return the installation path or <code>null</code>, if no installation 396 * was found or if an error occurred 397 */ getPathFromSVersionFile()398 private static String getPathFromSVersionFile() { 399 400 final String SVERSION = ".sversionrc"; // Unix/Linux only 401 final String VERSIONS = "[Versions]"; 402 403 String path = null; 404 405 try { 406 File fSVersion = new File( 407 System.getProperty( "user.home" ) ,SVERSION ); 408 if ( fSVersion.exists() ) { 409 Vector lines = new Vector(); 410 BufferedReader br = null; 411 try { 412 br = new BufferedReader( new InputStreamReader( 413 new FileInputStream( fSVersion ), "UTF-8" ) ); 414 String line = null; 415 while ( ( line = br.readLine() ) != null && 416 ( line.equals( VERSIONS ) ) != true ) { 417 // read lines until [Versions] is found 418 } 419 while ( ( line = br.readLine() ) != null && 420 line.length() != 0 ) { 421 if ( !line.startsWith( ";" ) ) 422 lines.add( line ); 423 } 424 } catch ( IOException e ) { 425 // if an I/O exception is thrown, try to analyze the lines 426 // read so far 427 System.err.println( "com.sun.star.lib.loader." + 428 "InstallationFinder::getPathFromSVersionFile: " + 429 "reading .sversionrc file failed: " + e ); 430 } finally { 431 if ( br != null ) { 432 try { 433 br.close(); 434 } catch ( IOException e ) { 435 // closing .sversionrc failed, ignore 436 } 437 } 438 } 439 for ( int i = lines.size() - 1; i >= 0; i-- ) { 440 StringTokenizer tokens = new StringTokenizer( 441 (String)lines.elementAt( i ), "=" ); 442 if ( tokens.countTokens() != 2 ) 443 continue; 444 String key = tokens.nextToken(); 445 String url = tokens.nextToken(); 446 path = getCanonicalPathFromFileURL( url ); 447 if ( path != null ) 448 break; 449 } 450 } 451 } catch ( SecurityException e ) { 452 return null; 453 } 454 455 return path; 456 } 457 458 /** 459 * Translates an OOo-internal absolute file URL reference (encoded using 460 * UTF-8) into a Java canonical pathname. 461 * 462 * @param oooUrl any URL reference; any fragment part is ignored 463 * 464 * @return if the given URL is a valid absolute, local (that is, the host 465 * part is empty or equal to "localhost", ignoring case) file URL, it is 466 * converted into an absolute canonical pathname; otherwise, 467 * <code>null</code> is returned 468 */ getCanonicalPathFromFileURL( String oooUrl )469 private static String getCanonicalPathFromFileURL( String oooUrl ) { 470 471 String prefix = "file://"; 472 if (oooUrl.length() < prefix.length() 473 || !oooUrl.substring(0, prefix.length()).toLowerCase().equals( 474 prefix)) 475 { 476 return null; 477 } 478 StringBuffer buf = new StringBuffer(prefix); 479 int n = oooUrl.indexOf('/', prefix.length()); 480 if (n < 0) { 481 n = oooUrl.length(); 482 } 483 String host = oooUrl.substring(prefix.length(), n); 484 if (host.length() != 0 && !host.toLowerCase().equals("localhost")) { 485 return null; 486 } 487 buf.append(host); 488 if (n == oooUrl.length()) { 489 buf.append('/'); 490 } else { 491 loop: 492 while (n < oooUrl.length()) { 493 buf.append('/'); 494 ++n; 495 int n2 = oooUrl.indexOf('/', n); 496 if (n2 < 0) { 497 n2 = oooUrl.length(); 498 } 499 while (n < n2) { 500 char c = oooUrl.charAt(n); 501 switch (c) { 502 case '%': 503 byte[] bytes = new byte[(n2 - n) / 3]; 504 int len = 0; 505 while (oooUrl.length() - n > 2 506 && oooUrl.charAt(n) == '%') 507 { 508 int d1 = Character.digit(oooUrl.charAt(n + 1), 16); 509 int d2 = Character.digit(oooUrl.charAt(n + 2), 16); 510 if (d1 < 0 || d2 < 0) { 511 break; 512 } 513 int d = 16 * d1 + d2; 514 if (d == '/') { 515 return null; 516 } 517 bytes[len++] = (byte) d; 518 n += 3; 519 } 520 String s; 521 try { 522 s = new String(bytes, 0, len, "UTF-8"); 523 } catch (UnsupportedEncodingException e) { 524 return null; 525 } 526 buf.append(s); 527 break; 528 529 case '#': 530 break loop; 531 532 default: 533 buf.append(c); 534 ++n; 535 break; 536 } 537 } 538 } 539 } 540 URL url; 541 try { 542 url = new URL(buf.toString()); 543 } catch (MalformedURLException e) { 544 return null; 545 } 546 String path = url.getFile(); 547 String fragment = url.getRef(); 548 if (fragment != null) { 549 path += '#' + fragment; 550 } 551 String ret = null; 552 File file = new File( path, SOFFICE ); 553 try { 554 if ( file.isAbsolute() && file.exists() ) { 555 try { 556 // resolve symlink 557 ret = file.getCanonicalFile().getParent(); 558 } catch ( IOException e ) { 559 return null; 560 } 561 } 562 } catch ( SecurityException e ) { 563 return null; 564 } 565 566 return ret; 567 } 568 569 /** 570 This class is used for emptying any stream which is passed into it in 571 a separate thread. 572 */ 573 private static final class StreamGobbler extends Thread { 574 575 InputStream m_istream; 576 StreamGobbler( InputStream istream )577 StreamGobbler( InputStream istream ) { 578 m_istream = istream; 579 } 580 run()581 public void run() { 582 try { 583 BufferedReader br = new BufferedReader( 584 new InputStreamReader( m_istream ) ); 585 // read from input stream 586 while ( br.readLine() != null ) { 587 // don't handle line content 588 } 589 br.close(); 590 } catch ( IOException e ) { 591 // stop reading from input stream 592 } 593 } 594 } 595 } 596