1 /*
   2  * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.servicetag;
  27 
  28 import java.io.*;
  29 import java.util.Date;
  30 import java.text.SimpleDateFormat;
  31 import java.text.ParseException;
  32 import java.util.TimeZone;
  33 import java.util.UUID;
  34 import java.lang.reflect.Field;
  35 import java.lang.reflect.Method;
  36 
  37 // Utility class for com.sun.servicetag package
  38 class Util {
  39     private static boolean verbose = (System.getProperty("servicetag.verbose") != null);
  40     private static String jrepath = null;
  41     private static final String REGKEY_TAIL =
  42         "microsoft\\windows\\currentversion\\app paths\\stclient.exe";
  43     private static final String STCLIENT_TAIL =  "sun\\servicetag\\stclient.exe";
  44     private static final String WIN32_STCLIENT =
  45         "c:\\Program Files (x86)\\" + STCLIENT_TAIL;
  46 
  47     // for debugging and tracing
  48     static boolean isVerbose() {
  49         return verbose;
  50     }
  51 
  52     /**
  53      * Gets the pathname of JRE in the running platform
  54      * This can be a JDK or JRE.
  55      */
  56     static synchronized String getJrePath() {
  57         if (jrepath == null) {
  58             // Determine the JRE path by checking the existence of
  59             // <HOME>/jre/lib and <HOME>/lib.
  60             String javaHome = System.getProperty("java.home");
  61             jrepath = javaHome + File.separator + "jre";
  62             File f = new File(jrepath, "lib");
  63             if (!f.exists()) {
  64                 // java.home usually points to the JRE path
  65                 jrepath = javaHome;
  66             }
  67         }
  68         return jrepath;
  69     }
  70 
  71     /**
  72      * Tests if the running platform is a JDK.
  73      */
  74     static boolean isJdk() {
  75         // <HOME>/jre exists which implies it's a JDK
  76         return getJrePath().endsWith(File.separator + "jre");
  77     }
  78 
  79     /**
  80      * Generates the URN string of "urn:st" namespace
  81      */
  82     static String generateURN() {
  83         return "urn:st:" + UUID.randomUUID().toString();
  84     }
  85 
  86     static int getIntValue(String value) {
  87         try {
  88             return Integer.parseInt(value);
  89         } catch (NumberFormatException e) {
  90             throw new IllegalArgumentException("\"" + value + "\"" +
  91                 " expected to be an integer");
  92         }
  93     }
  94 
  95     /**
  96      * Formats the Date into a timestamp string in YYYY-MM-dd HH:mm:ss GMT.
  97      * @param timestamp Date
  98      * @return a string representation of the timestamp
  99      *         in the YYYY-MM-dd HH:mm:ss GMT format.
 100      */
 101     static String formatTimestamp(Date timestamp) {
 102         if (timestamp == null) {
 103             return "[No timestamp]";
 104         }
 105         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
 106         df.setTimeZone(TimeZone.getTimeZone("GMT"));
 107         return df.format(timestamp);
 108     }
 109 
 110     /**
 111      * Parses a timestamp string in YYYY-MM-dd HH:mm:ss GMT format.
 112      * @param timestamp Timestamp in the YYYY-MM-dd HH:mm:ss GMT format.
 113      * @return Date
 114      */
 115     static Date parseTimestamp(String timestamp) {
 116         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
 117         df.setTimeZone(TimeZone.getTimeZone("GMT"));
 118         try {
 119             return df.parse(timestamp);
 120         } catch (ParseException e) {
 121             // should not reach here
 122             e.printStackTrace();
 123             return new Date();
 124         }
 125     }
 126 
 127     static String commandOutput(Process p) throws IOException {
 128         Reader r = null;
 129         Reader err = null;
 130         try {
 131             r = new InputStreamReader(p.getInputStream());
 132             err = new InputStreamReader(p.getErrorStream());
 133             String output = commandOutput(r);
 134             String errorMsg = commandOutput(err);
 135             p.waitFor();
 136             return output + errorMsg.trim();
 137         } catch (InterruptedException e) {
 138             if (isVerbose()) {
 139                 e.printStackTrace();
 140             }
 141             return e.getMessage();
 142         } finally {
 143             try {
 144                 if (r != null) {
 145                     r.close();
 146                 }
 147             } finally {
 148                 if (err != null) {
 149                     err.close();
 150                 }
 151             }
 152         }
 153     }
 154 
 155     static String commandOutput(Reader r) throws IOException {
 156         StringBuilder sb = new StringBuilder();
 157         int c;
 158         while ((c = r.read()) > 0) {
 159             if (c != '\r') {
 160                 sb.append((char) c);
 161             }
 162         }
 163         return sb.toString();
 164     }
 165 
 166     static int getJdkVersion() {
 167         parseVersion();
 168         return jdkVersion;
 169     }
 170 
 171     static int getUpdateVersion() {
 172         parseVersion();
 173         return jdkUpdate;
 174     }
 175 
 176     private static int jdkVersion = 0;
 177     private static int jdkUpdate = 0;
 178     private static synchronized void parseVersion() {
 179         if (jdkVersion > 0) {
 180             return;
 181         }
 182 
 183         // parse java.runtime.version
 184         // valid format of the version string is:
 185         // n.n.n[_uu[c]][-<identifer>]-bxx
 186         String cs = System.getProperty("java.runtime.version");
 187         if (cs.length() >= 5 &&
 188             Character.isDigit(cs.charAt(0)) && cs.charAt(1) == '.' &&
 189             Character.isDigit(cs.charAt(2)) && cs.charAt(3) == '.' &&
 190             Character.isDigit(cs.charAt(4))) {
 191             jdkVersion = Character.digit(cs.charAt(2), 10);
 192             cs = cs.substring(5, cs.length());
 193             if (cs.charAt(0) == '_' && cs.length() >= 3 &&
 194                 Character.isDigit(cs.charAt(1)) &&
 195                 Character.isDigit(cs.charAt(2))) {
 196                 int nextChar = 3;
 197                 try {
 198                     String uu = cs.substring(1, 3);
 199                     jdkUpdate = Integer.valueOf(uu).intValue();
 200                 } catch (NumberFormatException e) {
 201                     // not conforming to the naming convention
 202                     return;
 203                 }
 204             }
 205         } else {
 206             throw new InternalError("Invalid java.runtime.version" + cs);
 207         }
 208     }
 209 
 210     /**
 211      * Returns this java string as a null-terminated byte array
 212      */
 213     private static byte[] stringToByteArray(String str) {
 214         return (str + "\u0000").getBytes();
 215     }
 216 
 217     /**
 218      * Converts a null-terminated byte array to java string
 219      */
 220     private static String byteArrayToString(byte[] array) {
 221         return new String(array, 0, array.length -1);
 222     }
 223 
 224     /**
 225      * Gets the stclient path using a well known location from
 226      * the Windows platform Registry, ensuring the path returned
 227      * by the registry is really the one we are looking for,
 228      * otherwise it will return null.
 229      */
 230     private static File getWindowsStClientFile(boolean wow64) {
 231         File out = null;
 232         String regKey = (wow64 == true)
 233             ? "software\\Wow6432Node\\" + REGKEY_TAIL
 234             : "software\\" + REGKEY_TAIL;
 235         String keyName = "" ; // use the default  key
 236         String path = getRegistryKey(regKey, keyName);
 237         if (path != null
 238                 && (new File(path)).exists()
 239                 && path.toLowerCase().endsWith(STCLIENT_TAIL.toLowerCase())) {
 240             out = new File(path);
 241         }
 242         if (isVerbose()) {
 243             System.out.println("stclient=" + out);
 244         }
 245         return out;
 246     }
 247 
 248     /**
 249      * Finds a stclient in 32 and 64 bit environments, first by querying
 250      * the windows registry, if not then get the well known paths for
 251      * 64bit see http://support.microsoft.com/kb/896459
 252      */
 253 
 254     static File getWindowsStClientFile() {
 255         File stclient = null;
 256         if (System.getProperty("os.arch").equals("x86")) {
 257             // try to get the default entry
 258             stclient = getWindowsStClientFile(false);
 259             if (stclient != null) {
 260                 return stclient;
 261             }
 262         } else { // we are on 64-bit system
 263             // try the wow64 area
 264             stclient = getWindowsStClientFile(true);
 265             if (stclient != null) {
 266                 return stclient;
 267             }
 268             // try the default hard coded path, maybe wow64 registry is missing
 269             stclient = new File(WIN32_STCLIENT);
 270             if (stclient.canExecute()) {
 271                 if (isVerbose()) {
 272                     System.out.println("stclient(default)=" + stclient);
 273                 }
 274                 return stclient;
 275             }
 276         }
 277         if (isVerbose()) {
 278             System.out.println("stclient not found");
 279         }
 280         return null;
 281     }
 282 
 283     /**
 284      * This uses reflection to access a private java windows registry
 285      * interface, any changes to that Class must be appropriately adjusted.
 286      * Returns a null if unsuccessful.
 287      */
 288     private static String getRegistryKey(String regKey, String keyName) {
 289         String out = null;
 290         try {
 291             Class<?> clazz = Class.forName("java.util.prefs.WindowsPreferences");
 292 
 293             // Get the registry methods
 294             Method winRegOpenKeyM = clazz.getDeclaredMethod("WindowsRegOpenKey",
 295                     int.class, byte[].class, int.class);
 296             winRegOpenKeyM.setAccessible(true);
 297 
 298             Method winRegCloseKeyM = clazz.getDeclaredMethod("WindowsRegCloseKey",
 299                     int.class);
 300             winRegCloseKeyM.setAccessible(true);
 301 
 302             Method winRegQueryValueM = clazz.getDeclaredMethod("WindowsRegQueryValueEx",
 303                     int.class, byte[].class);
 304             winRegQueryValueM.setAccessible(true);
 305 
 306             // Get all the constants we need
 307             int HKLM = getValueFromStaticField("HKEY_LOCAL_MACHINE", clazz);
 308             int KEY_READ = getValueFromStaticField("KEY_READ", clazz);
 309             int ERROR_CODE = getValueFromStaticField("ERROR_CODE", clazz);
 310             int NATIVE_HANDLE = getValueFromStaticField("NATIVE_HANDLE", clazz);
 311             int ERROR_SUCCESS = getValueFromStaticField("ERROR_SUCCESS", clazz);
 312 
 313             // Convert keys
 314             byte[] reg = stringToByteArray(regKey);
 315             byte[] key = stringToByteArray(keyName);
 316 
 317             // Open the registry
 318             int[] result = (int[]) winRegOpenKeyM.invoke(null, HKLM, reg, KEY_READ);
 319 
 320             if (result[ERROR_CODE] == ERROR_SUCCESS) {
 321                 byte[] stvalue = (byte[]) winRegQueryValueM.invoke(null,
 322                     result[NATIVE_HANDLE], key);
 323                 out = byteArrayToString(stvalue);
 324                 winRegCloseKeyM.invoke(null, result[NATIVE_HANDLE]);
 325             }
 326         } catch (Exception ex) {
 327             if (isVerbose()) {
 328                 ex.printStackTrace();
 329             }
 330         }
 331         return out;
 332     }
 333 
 334     private static int getValueFromStaticField(String fldName, Class<?> klass) throws Exception {
 335         Field f = klass.getDeclaredField(fldName);
 336         f.setAccessible(true);
 337         return f.getInt(null);
 338     }
 339 }