/******************************************************************************* * Copyright (c) May 18, 2011 Zend Technologies Ltd. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.zend.sdklib.internal.target; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.security.SecureRandom; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.zend.sdklib.SdkException; import org.zend.sdklib.internal.utils.EnvironmentUtils; import org.zend.sdklib.internal.utils.json.JSONArray; import org.zend.sdklib.internal.utils.json.JSONException; import org.zend.sdklib.internal.utils.json.JSONObject; import org.zend.sdklib.manager.DetectionException; import org.zend.sdklib.target.IZendTarget; import org.zend.webapi.core.WebApiClient; import org.zend.webapi.core.WebApiException; import org.zend.webapi.core.connection.auth.BasicCredentials; import org.zend.webapi.core.connection.data.VhostInfo; import org.zend.webapi.core.connection.data.VhostsList; import org.zend.webapi.core.connection.data.values.ServerType; import org.zend.webapi.core.connection.data.values.ZendServerVersion; import com.ice.jni.registry.NoSuchKeyException; import com.ice.jni.registry.Registry; import com.ice.jni.registry.RegistryException; import com.ice.jni.registry.RegistryKey; import com.ice.jni.registry.RegistryValue; /** * Auto detect local server * * @author Roy, 2011 */ public class ZendTargetAutoDetect { private static final String AUTHOPEN = "/usr/libexec/authopen"; //$NON-NLS-1$ private static final String INSTALL_LOCATION = "InstallLocation"; //$NON-NLS-1$ private static final String USER_INI = "zend-server-user.ini"; //$NON-NLS-1$ private static final String NEED_TO_ELEVATE = "You need root privileges to run this script!"; //$NON-NLS-1$ private static final String MISSING_ZEND_SERVER = "Local Zend Server couldn't be found. Install it first before add it as a target." //$NON-NLS-1$ + "\nFor more details refer to http://www.zend.com/server."; //$NON-NLS-1$ // linux key private static final String CONFIG_FILE_LINUX = "/etc/zce.rc"; //$NON-NLS-1$ private static final String CONFIG_FILE_LINUX_DEB = "/etc/zce.rc-deb"; //$NON-NLS-1$ private static final String CONFIG_FILE_LINUX_RPM = "/etc/zce.rc-rpm"; //$NON-NLS-1$ private static final String ZCE_PREFIX = "ZCE_PREFIX"; //$NON-NLS-1$ private static final String APACHE_PORT = "APACHE_PORT"; //$NON-NLS-1$ private static final String PRODUCT_VERSION = "PRODUCT_VERSION"; //$NON-NLS-1$ // Registry private static final String NODE_64 = "WOW6432node"; //$NON-NLS-1$ private static final String ZEND_SERVER = "ZendServer"; //$NON-NLS-1$ private static final String APACHE_APP_PORT = "ApacheAppPort"; //$NON-NLS-1$ private static final String IIS_APP_PORT = "IISAppPort"; //$NON-NLS-1$ private static final String ZEND_TECHNOLOGIES = "Zend Technologies"; //$NON-NLS-1$ private static final String VERSION = "Version"; //$NON-NLS-1$ /** * Set this to true if you agree to get GUI dialogs, such as privileges * elevation dialog on MacOS. Set this to false otherwise, e.g. if you're * running from command line and don't want to open any dialogs. */ public static boolean CAN_OPEN_GUI_DIALOGS = false; public static URL localhost = null; private String zendServerInstallLocation = null; private Process macElevatedWrite; static { try { localhost = new URL("http://localhost:10081"); //$NON-NLS-1$ } catch (MalformedURLException e) { // ignore localhost is a valid URL } } public static int converByteArrayToInt(byte[] byteData) { int result = 0; for (int i = 0; i < byteData.length; i++) { result += (int) (byteData[i] & 0xFF) << (8 * (byteData.length - i - 1)); } return result != 0 ? result : -1; } public ZendTargetAutoDetect() { this(true); } public ZendTargetAutoDetect(boolean init) { if (init) { init(); } } public void init() { if (zendServerInstallLocation == null) { zendServerInstallLocation = findLocalhostInstallDirectory(); } } /** * * @param targetId * @param key * @return the new target * @throws IOException */ public IZendTarget createLocalhostTarget(String targetId, String key) throws IOException { // find localhost install directory String secretKey = findExistingSecretKey(key); if (secretKey == null) { secretKey = applySecretKey(key, generateSecretKey()); } return createLocalhostTarget(targetId, key, secretKey); } /** * * @param targetId * @param key * @return the new target * @throws IOException */ public IZendTarget createLocalhostTarget(String targetId, String key, String secretKey) throws IOException { // create the target return new ZendTarget(targetId, localhost, getDefaultServerURL(key, secretKey), key, secretKey); } /** * Generates a secret key and assigns locally. this key is not yet applied * to the local zend server. Read * {@link ZendTargetAutoDetect#applySecretKey(String, String)} to apply the * generated key * * @param targetId * @param key * @return the new target */ public IZendTarget createTemporaryLocalhost(String targetId, String key) { final String sk = generateSecretKey(); return new ZendTarget(targetId, localhost, getDefaultServerURL(key, sk), key, sk, true); } /** * Apply key and secret key to the installed local server * * @param key * @param secretKey * @return * @throws IOException * @throws FileNotFoundException */ public String applySecretKey(String key, String secretKey) throws IOException, FileNotFoundException { File keysFile = getApiKeysFile(); // assert permissions are elevated if (!keysFile.canWrite() && !(EnvironmentUtils.isUnderMaxOSX() && canElevatedWriteOnMac())) { throw new IOException(NEED_TO_ELEVATE); } // temporary file final File edited = File.createTempFile(USER_INI, ".tmp"); //$NON-NLS-1$ if (!edited.exists()) { edited.createNewFile(); } // backup file BufferedReader ir = new BufferedReader(new FileReader(keysFile)); PrintStream os = new PrintStream(edited); copyWithoutEdits(ir, os); ir.close(); os.close(); if (EnvironmentUtils.isUnderMaxOSX() && canElevatedWriteOnMac()) { os = new PrintStream(openElevatedWriteOnMac(keysFile)); } else { // the normal way os = new PrintStream(keysFile); } // write zend-server-users.ini and find key ir = new BufferedReader(new FileReader(edited)); secretKey = copyWithEdits(ir, os, key, secretKey); ir.close(); os.close(); if (macElevatedWrite != null) { try { macElevatedWrite.waitFor(); } catch (InterruptedException e) { // ignore } } return trimQuotes(secretKey); } private OutputStream openElevatedWriteOnMac(File keysFile) throws IOException { macElevatedWrite = Runtime.getRuntime().exec(new String[] { AUTHOPEN, "-w", keysFile.toString() }); //$NON-NLS-1$ return macElevatedWrite.getOutputStream(); } private boolean canElevatedWriteOnMac() { return new File(AUTHOPEN).exists() && CAN_OPEN_GUI_DIALOGS; } public static String copyWithEdits(BufferedReader ir, PrintStream os, String key, String secretKey) throws IOException { String line = ir.readLine(); boolean block = false; while (line != null) { if ("[apiKeys]".equals(line)) { //$NON-NLS-1$ writeApiKeyBlock(key, os, secretKey); block = true; } else { os.println(line); } line = ir.readLine(); } if (!block) { writeApiKeyBlock(key, os, secretKey); } return secretKey; } public static void copyWithoutEdits(BufferedReader ir, PrintStream os) throws IOException { String line = ir.readLine(); while (line != null) { os.println(line); line = ir.readLine(); } } public static Properties readApiKeysSection(BufferedReader reader) throws IOException { Properties properties = new Properties(); String line = reader.readLine(); while (line != null) { if ("[apiKeys]".equals(line)) { //$NON-NLS-1$ line = reader.readLine(); while (line != null && !line.startsWith("[")) { //$NON-NLS-1$ final String[] split = line.split("="); //$NON-NLS-1$ if (split != null && split.length == 2) { properties.put(split[0].trim(), split[1].trim()); } line = reader.readLine(); } } line = reader.readLine(); } return properties; } public static Map<String, String> parseApiKey(String input) throws SdkException { Map<String, String> result = new HashMap<String, String>(); try { JSONObject json = new JSONObject(input); json = ((JSONObject) json.get("responseData")); //$NON-NLS-1$ JSONArray keys = ((JSONArray) json.get("keys")); //$NON-NLS-1$ if (keys != null && keys.length() > 0) { int size = keys.length(); for (int i = 0; i < size; i++) { json = (JSONObject) keys.get(i); String name = (String) json.get("name"); //$NON-NLS-1$ String secretKey = (String) json.get("hash"); //$NON-NLS-1$ result.put(name, secretKey); } } } catch (JSONException e) { throw new SdkException(e); } return result; } /** * Returns a new target for the localhost target * * @param key * @return * @throws IOException */ public String findExistingSecretKey(String key) throws IOException { // assert permissions are elevated File keysFile = getApiKeysFile(); if (!keysFile.canRead()) { // "Permission denied" throw new IOException(NEED_TO_ELEVATE); } // read zend-server-users.ini and find key final BufferedReader reader = new BufferedReader(new FileReader(keysFile)); Properties p = readApiKeysSection(reader); reader.close(); final String hash = p.getProperty(key + ":hash"); //$NON-NLS-1$ if (hash != null) { // return secretKey if possible return trimQuotes(hash); } // key not found return null; } protected String trimQuotes(final String hash) { return hash.startsWith("\"") ? hash.substring(1, hash.length() - 1) : hash; //$NON-NLS-1$ } /** * @return returns location of the local Zend Server instance, null if no * local Zend Server installed. */ public String findLocalhostInstallDirectory() { String result = null; if (EnvironmentUtils.isUnderLinux() || EnvironmentUtils.isUnderMaxOSX()) { result = getLocalZendServerFromFile(); } else { result = getLocalZendServerFromRegistry(); } if (result == null) { throw new IllegalStateException(MISSING_ZEND_SERVER); } return result; } private String getLocalZendServerFromFile() { Properties props = getLocalZendServerProperties(); return props != null ? props.getProperty(ZCE_PREFIX) : null; } private Properties getLocalZendServerProperties() { Properties props = null; // Try to find the zend.rc-deb file. try { FileInputStream fileStream = new FileInputStream(CONFIG_FILE_LINUX_DEB); props = new Properties(); props.load(fileStream); } catch (FileNotFoundException e) { } catch (IOException e) { } // If not found, find the zend.rc-rpm file. if (props == null) { try { FileInputStream fileStream = new FileInputStream(CONFIG_FILE_LINUX_RPM); props = new Properties(); props.load(fileStream); } catch (FileNotFoundException e) { } catch (IOException e) { } } // if not found, find the zend.rc file. if (props == null) { try { FileInputStream fileStream = new FileInputStream(CONFIG_FILE_LINUX); props = new Properties(); props.load(fileStream); } catch (FileNotFoundException e) { } catch (IOException e) { } } return props; } private String getLocalZendServerFromRegistry() { try { RegistryKey zendServerKey = getZendServerRegistryKey(); if (zendServerKey != null) { return zendServerKey.getStringValue(INSTALL_LOCATION); } } catch (RegistryException e) { return null; } return null; } private RegistryKey getZendServerRegistryKey() throws RegistryException { RegistryKey zendServerKey = null; try { zendServerKey = Registry.HKEY_LOCAL_MACHINE.openSubKey("SOFTWARE").openSubKey(ZEND_TECHNOLOGIES) //$NON-NLS-1$ .openSubKey(ZEND_SERVER); return zendServerKey; } catch (NoSuchKeyException e1) { // try the 64 bit try { zendServerKey = Registry.HKEY_LOCAL_MACHINE.openSubKey("SOFTWARE").openSubKey(NODE_64) //$NON-NLS-1$ .openSubKey(ZEND_TECHNOLOGIES).openSubKey(ZEND_SERVER); return zendServerKey; } catch (NoSuchKeyException e) { return null; } } } private static void writeApiKeyBlock(String key, PrintStream os, final String sk) { os.println("[apiKeys]"); //$NON-NLS-1$ // roy:name = "roy" os.print(key); os.print(":name = \""); //$NON-NLS-1$ os.print(key); os.println("\""); //$NON-NLS-1$ // roy:creationTime = 1304968104 os.print(key); os.print(":creationTime = "); //$NON-NLS-1$ os.println(new Date().getTime() / 1000); // roy:hash = "c86ba2bc5fb62ee916031cf78..." os.print(key); os.print(":hash = \""); //$NON-NLS-1$ os.print(sk); os.println("\""); //$NON-NLS-1$ // roy:role = "fullAccess" os.print(key); os.println(":role = \"fullAccess\""); //$NON-NLS-1$ os.println(); } private File getApiKeysFile() { File keysFile = null; if (EnvironmentUtils.isUnderLinux() || EnvironmentUtils.isUnderMaxOSX()) { keysFile = new File(zendServerInstallLocation + "/gui/application/data/zend-server-user.ini"); //$NON-NLS-1$ } else if (EnvironmentUtils.isUnderWindows()) { keysFile = new File(zendServerInstallLocation + "ZendServer\\GUI\\application\\data\\zend-server-user.ini"); //$NON-NLS-1$ } if (keysFile == null) { throw new IllegalStateException(MISSING_ZEND_SERVER); } if (!keysFile.exists()) { throw new IllegalArgumentException("The file " + keysFile.getAbsolutePath() + " does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ } return keysFile; } private static String generateSecretKey() { SecureRandom random = new SecureRandom(); final BigInteger bigInteger = new BigInteger(256, random); final String string = bigInteger.toString(16); return string.length() == 64 ? string : pad(string, 64); } /** * Random number was prefixed with some zeros... pad it * * @param string * @param i * @return */ final private static String pad(String string, int i) { i = i - string.length(); StringBuilder builder = new StringBuilder(string); for (int j = 0; j < i; j++) { builder.append("0"); //$NON-NLS-1$ } return builder.toString(); } private URL getDefaultServerURL(String key, String secretKey) { String versionString = null; try { if (EnvironmentUtils.isUnderLinux() || EnvironmentUtils.isUnderMaxOSX()) { Properties props = getLocalZendServerProperties(); if (props != null) { versionString = props.getProperty(PRODUCT_VERSION, null); } } else { // (EnvironmentUtils.isUnderWindows()) RegistryKey zendServerKey = getZendServerRegistryKey(); if (zendServerKey != null) { versionString = zendServerKey.getStringValue(VERSION); } } if(isIISBased()) return getDefaultIISServerURL(); ZendServerVersion version = ZendServerVersion.byName(versionString); if (ZendServerVersion.v6_2_x.compareTo(version) < -1) return getDefaultServerURLFromConfig(); return getDefaultServerURLWithAPI(key, secretKey); } catch (Exception e) { // if any exception occurs ignore it and return localhost } return localhost; } private boolean isIISBased() { if(!EnvironmentUtils.isUnderWindows()) return false; try { RegistryKey zendServerKey = getZendServerRegistryKey(); RegistryValue portValue = zendServerKey.getValue(IIS_APP_PORT); return (portValue != null); } catch (RegistryException e) { } return false; } private URL getDefaultIISServerURL() throws RegistryException, MalformedURLException { int port = -1; RegistryKey zendServerKey = getZendServerRegistryKey(); if (zendServerKey != null) { RegistryValue portValue = zendServerKey.getValue(IIS_APP_PORT); if (portValue != null) { port = converByteArrayToInt(portValue.getByteData()); } } return new URL(localhost.getProtocol(), localhost.getHost(), port, ""); //$NON-NLS-1$ } private URL getDefaultServerURLWithAPI(String key, String secretKey) throws MalformedURLException, WebApiException, DetectionException { WebApiClient apiClient = new WebApiClient(new BasicCredentials(key, secretKey), localhost.toString()); apiClient.setServerType(ServerType.ZEND_SERVER); VhostsList vhostsList = apiClient.vhostGetStatus(); for (VhostInfo vhostInfo : vhostsList.getVhosts()) { if(!vhostInfo.isDefaultVhost()) continue; return new URL(localhost.getProtocol(), localhost.getHost(), vhostInfo.getPort(), ""); //$NON-NLS-1$ } throw new DetectionException("No default virtual host found"); //$NON-NLS-1$ } private URL getDefaultServerURLFromConfig() throws RegistryException, MalformedURLException { int port = -1; if (EnvironmentUtils.isUnderLinux() || EnvironmentUtils.isUnderMaxOSX()) { Properties props = getLocalZendServerProperties(); if (props != null) { port = Integer.valueOf(props.getProperty(APACHE_PORT, "-1")); //$NON-NLS-1$ } } else { // (EnvironmentUtils.isUnderWindows()) RegistryKey zendServerKey = getZendServerRegistryKey(); if (zendServerKey != null) { RegistryValue portValue = zendServerKey.getValue(APACHE_APP_PORT); if (portValue != null) { port = converByteArrayToInt(portValue.getByteData()); } } } return new URL(localhost.getProtocol(), localhost.getHost(), port, ""); //$NON-NLS-1$ } }