/*******************************************************************************
* Copyright (c) 2014 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
*
* Contributors:
* Zend Technologies Ltd. - initial API and implementation
*******************************************************************************/
package org.zend.php.server.core.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
/**
* Utility class for detecting local Apache HTTP Server settings. It works
* correctly with following Apache Server distributions: <h4>Windows</h4>
* <ul>
* <li>httpd binary (<a
* href="http://httpd.apache.org/download.cgi">http://httpd.
* apache.org/download.cgi</a>)</li>
* <li>WAMP Server (<a
* href="http://www.wampserver.com">http://www.wampserver.com</a>)</li>
* <li>Bitnami WAMPStack (<a
* href="https://bitnami.com/stack/wamp">https://bitnami.com/stack/wamp</a>)</li>
* <li>XAMPP (<a
* href="http://www.apachefriends.org">http://www.apachefriends.org</a>)</li>
* </ul>
* <h4>Linux</h4>
* <ul>
* <li>httpd binary (<a
* href="http://httpd.apache.org/download.cgi">http://httpd.
* apache.org/download.cgi</a>)</li>
* <li>installed through apt (Debian)</li>
* </ul>
* <h4>Mac OS X</h4>
* <ul>
* <li>httpd binary (<a
* href="http://httpd.apache.org/download.cgi">http://httpd.
* apache.org/download.cgi</a>)</li>
* <li>Default Apache Server provided with operating system</li>
* <li>MAMP (<a href="http://www.mamp.info">http://www.mamp.info</a>)</li>
* <li>XAMPP (<a
* href="http://www.apachefriends.org">http://www.apachefriends.org</a>)</li>
* <li>AMPPS (<a href="http://ampps.com/">http://ampps.com</a>)</li>
* </ul>
*
* @author Wojciech Galanciak, 2014
*
*/
public class LocalApacheDetector {
public static final String ID = "org.zend.php.server.ui.types.LocalApacheType"; //$NON-NLS-1$
public static final String LOCATION = "apache2Location"; //$NON-NLS-1$
private static final String LISTEN = "Listen"; //$NON-NLS-1$
private static final String DOCUMENT_ROOT = "DocumentRoot"; //$NON-NLS-1$
// Common
private static final String BIN = "bin"; //$NON-NLS-1$
private static final String CONF = "conf"; //$NON-NLS-1$
private static final String APACHE = "apache"; //$NON-NLS-1$
private static final String HTTPD_CONF = "httpd.conf"; //$NON-NLS-1$
// Linux
private static final String DEBIAN_PORT_CONF = "/ports.conf"; //$NON-NLS-1$
private static final String DEBIAN_DEFAULT_CONF = "/sites-enabled/000-default.conf"; //$NON-NLS-1$
// Windows
private static final String XAMPP_START_EXE = "xampp_start.exe"; //$NON-NLS-1$
private static final String WAMPMANAGER_EXE = "wampmanager.exe"; //$NON-NLS-1$
// Mac
private static final String APACHE_2 = "apache2"; //$NON-NLS-1$
private static final String MAMP_APP = "MAMP.app"; //$NON-NLS-1$
private static final String ETC = "etc"; //$NON-NLS-1$
private String port;
private String documentRoot;
private String location;
public LocalApacheDetector(String location) {
super();
this.location = location;
}
/**
* Detect local Apache HTTP Server configuration in specified location.
*
* @return <code>true</code> if configuration was detected successfully;
* otherwise return <code>false</code>
*/
public boolean detect() {
if (location != null) {
File httpdConf = new File(location, getPath(CONF, HTTPD_CONF));
// firstly check a default configuration location (/conf/httpd.conf)
if (httpdConf.exists()) {
parseHttpdConf(httpdConf);
} else {
if (Platform.getOS().equals(Platform.OS_WIN32)) {
if (checkWAMP(location)) {
return true;
}
if (checkBitnamiWAMP(location)) {
return true;
}
if (checkXAMPPWin32(location)) {
return true;
}
}
if (Platform.getOS().equals(Platform.OS_LINUX)) {
if (checkDebian(location)) {
return true;
}
}
if (Platform.getOS().equals(Platform.OS_MACOSX)) {
if (checkMAMP(location)) {
return true;
}
if (checkXAMPPMac(location)) {
return true;
}
}
// check if there is apache2 folder in specified location
if (checkApache2(location)) {
return true;
}
// finally check httpd.conf in specified location
httpdConf = new File(location, HTTPD_CONF);
if (httpdConf.exists()) {
return parseHttpdConf(httpdConf);
}
}
}
return port != null && documentRoot != null;
}
/**
* Return value of Listen server setting.
*
* @return detected port
*/
public String getPort() {
return port;
}
/**
* Return value of DocumentRoot server setting.
*
* @return detected document root
*/
public String getDocumentRoot() {
return documentRoot;
}
protected void setPort(String port) {
// check what is a format of Listen value;
// possible values:
// Listen 80
// Listen 0.0.0.0:80
// Listen [::0]:80
int index = port.indexOf(':');
if (index != -1) {
port = port.substring(index + 1);
}
this.port = port;
}
protected void setDocumentRoot(String documentRoot) {
this.documentRoot = documentRoot;
}
/**
* Try to detect local Apache Server configuration from MAMP distribution on
* Mac OS X. It tests following possible location passed as an argument:
* <ul>
* <li>MAMP's root directory (e.g. /Applications/MAMP)</li>
* <li>apache bin directory (e.g. /Applications/MAMP/bin/apache2)</li>
* </ul>
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkMAMP(String location) {
// check if it is MAMP's apache2 in bin (e.g.
// /Applications/MAMP/bin/apache2)
File mampApp = null;
File apache2 = new File(location);
if (APACHE_2.equals(apache2.getName())) {
apache2 = apache2.getParentFile();
if (apache2 != null) {
apache2 = apache2.getParentFile();
if (apache2 != null) {
mampApp = new File(apache2, MAMP_APP);
}
}
}
// check if it is MAMP's root (e.g. /Applications/MAMP)
if (mampApp == null) {
mampApp = new File(location, MAMP_APP);
}
if (mampApp.exists()) {
File httpdConf = new File(mampApp.getParent(), getPath(CONF,
APACHE, HTTPD_CONF));
if (httpdConf.exists()) {
return parseHttpdConf(httpdConf);
}
}
return false;
}
private boolean checkDebian(String location) {
// check debian configuration
File portsConf = new File(location, DEBIAN_PORT_CONF);
File defaultConf = new File(location, DEBIAN_DEFAULT_CONF);
if (portsConf.exists() && defaultConf.exists()) {
List<String> lines = readFile(portsConf);
for (String line : lines) {
if (line.startsWith(LISTEN)) {
parseListen(line);
break;
}
}
lines = readFile(defaultConf);
for (String line : lines) {
line = line.trim();
if (line.startsWith(DOCUMENT_ROOT)) {
parseDocumentRoot(line);
break;
}
}
}
return port != null && documentRoot != null;
}
/**
* Try to detect local Apache Server configuration from XAMPP distribution
* on Windows. It tests following possible location passed as an argument:
* <ul>
* <li>XAMPP's root directory (e.g. C:\xampp)</li>
* </ul>
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkXAMPPWin32(String location) {
// Check if it is a XAMPP's root (e.g. C:\xamp)
File xamppStart = new File(location, XAMPP_START_EXE);
if (xamppStart.exists()) {
File httpdConf = new File(location, getPath(APACHE, CONF,
HTTPD_CONF));
if (httpdConf.exists()) {
return parseHttpdConf(httpdConf);
}
}
return false;
}
/**
* Try to detect local Apache Server configuration from XAMPP distribution
* on Mac OS X. It tests following possible location passed as an argument:
* <ul>
* <li>XAMPP's root directory (e.g. /Applications/XAMPP)</li>
* </ul>
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkXAMPPMac(String location) {
// check if there is etc/httpd.conf file
File httpdConf = new File(location, getPath(ETC, HTTPD_CONF));
if (httpdConf.exists()) {
return parseHttpdConf(httpdConf);
}
return false;
}
/**
* Try to detect local Apache Server configuration from WAMP distribution on
* Windows. It tests following possible location passed as an argument:
* <ul>
* <li>WAMP's root directory (e.g. C:\wamp)</li>
* <li>apache bin directory (e.g. C:\wamp\bin\apache)</li>
* </ul>
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkWAMP(String location) {
// check if it is a WAMP's root (e.g. C:\wamp)
File wampManager = new File(location, WAMPMANAGER_EXE);
if (wampManager.exists()) {
return parseWAMP(location);
}
// Check if it apache's root (e.g. C:\wamp\bin\apache)
File apacheFile = new File(location);
if (APACHE.equals(apacheFile.getName())) {
wampManager = new File(apacheFile.getParentFile().getParent(),
WAMPMANAGER_EXE);
if (wampManager.exists()) {
return parseWAMP(wampManager.getParent());
}
}
return false;
}
/**
* Try to detect local Apache Server configuration from Bitnami WAMPStack
* distribution on Windows. It tests following possible location passed as
* an argument:
* <ul>
* <li>Bitnami WAMP's root folder (e.g. C:\Bitnami\wampstack-5.4.33-0)</li>
* </ul>
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkBitnamiWAMP(String location) {
return checkApache2(location);
}
/**
* Check if there is apache2 folder in specified location.
*
* @param location
* @return <code>true</code> if document root and port were detected
* correctly; otherwise return <code>false</code>
*/
private boolean checkApache2(String location) {
File httpdConf = new File(location, getPath(APACHE_2, CONF, HTTPD_CONF));
if (httpdConf.exists()) {
return parseHttpdConf(httpdConf);
}
return false;
}
private boolean parseWAMP(String location) {
File apacheRoot = new File(location, getPath(BIN, APACHE));
if (apacheRoot.exists()) {
String[] apacheFolders = apacheRoot.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(APACHE);
}
});
apacheRoot = new File(apacheRoot,
apacheFolders[apacheFolders.length - 1]);
File httpdConf = new File(apacheRoot, getPath(CONF, HTTPD_CONF));
return httpdConf.exists() && parseHttpdConf(httpdConf);
}
return false;
}
private List<String> readFile(File file) {
BufferedReader httpdReader = null;
List<String> result = new ArrayList<String>();
try {
httpdReader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = httpdReader.readLine()) != null) {
result.add(line);
}
} catch (IOException e) {
} finally {
if (httpdReader != null) {
try {
httpdReader.close();
} catch (IOException e) {
}
}
}
return result;
}
private void parseDocumentRoot(String line) {
String path = extractValue(line, DOCUMENT_ROOT);
if (path != null) {
if (path.startsWith("\"")) { //$NON-NLS-1$
path = path.substring(1, path.length() - 1);
}
setDocumentRoot(path);
}
}
/**
* Parse Listen and DocumentRoot from specified httpd.conf file.
*
* @param httpdConfFile
* httpd.conf file
* @return <code>true</code> file port and document root are correctly
* parsed; otherwise return <code>false</code>
*/
private boolean parseHttpdConf(File httpdConfFile) {
List<String> lines = readFile(httpdConfFile);
for (String line : lines) {
if (port == null && line.startsWith(LISTEN)) {
parseListen(line);
} else if (documentRoot == null && line.startsWith(DOCUMENT_ROOT)) {
parseDocumentRoot(line);
}
}
return port != null && documentRoot != null;
}
private void parseListen(String line) {
String value = extractValue(line, LISTEN);
if (value != null) {
setPort(value);
}
}
private String extractValue(String line, String attributeName) {
String path = line.trim().substring(attributeName.length());
return path.trim();
}
private String getPath(String... segments) {
if (segments.length == 0) {
return ""; //$NON-NLS-1$
}
IPath path = new Path(segments[0]);
for (int i = 1; i < segments.length; i++) {
path.append(segments[i]);
}
return path.toString();
}
}