/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain other free and open source software ("FOSS") code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.debug.internal.core.browsers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.PlatformUtils;
import com.aptana.ide.core.StringUtils;
import com.aptana.ide.debug.core.JSDebugPlugin;
/**
* @author Max Stepanov
*
*/
public final class Firefox {
public static final String NAME = "Firefox"; //$NON-NLS-1$
public static final String NEW_WINDOW = "-new-window"; //$NON-NLS-1$
public static final String NEW_TAB = "-new-tab"; //$NON-NLS-1$
private static final String[] WIN32_PROFILES_LOCATIONS = {
"%APPDATA%\\Mozilla\\Firefox\\" //$NON-NLS-1$
};
private static final String[] LINUX_PROFILES_LOCATIONS = {
"~/.mozilla/firefox/" //$NON-NLS-1$
};
private static final String[] MACOSX_PROFILES_LOCATIONS = {
"~/Library/Application Support/Firefox/", //$NON-NLS-1$
"~/Library/Mozilla/Firefox/" //$NON-NLS-1$
};
private static final Map<String, String[]> LOCATIONS = new HashMap<String, String[]>();
static {
LOCATIONS.put(Platform.OS_WIN32,WIN32_PROFILES_LOCATIONS);
LOCATIONS.put(Platform.OS_LINUX,LINUX_PROFILES_LOCATIONS);
LOCATIONS.put(Platform.OS_MACOSX,MACOSX_PROFILES_LOCATIONS);
}
private Firefox() {
}
/**
* isBrowserExecutable
*
* @param browserExecutable
* @return boolean
*/
public static boolean isBrowserExecutable(String browserExecutable) {
String name = new File(browserExecutable).getName();
if (name.toLowerCase().indexOf("firefox") != -1) { //$NON-NLS-1$
return true;
}
return false;
}
/**
* findDefaultProfileLocation
*
* @return File
*/
public static File findDefaultProfileLocation() {
String[] locations = (String[]) LOCATIONS.get(Platform.getOS());
if ( locations != null ) {
for( int i = 0; i < locations.length; ++i ) {
String location = PlatformUtils.expandEnvironmentStrings(locations[i]);
File dir = new File(location);
if ( !dir.isDirectory() || !dir.exists() ) {
continue;
}
IdeLog.logInfo(JSDebugPlugin.getDefault(),StringUtils.format("Check location {0} for default profile",location)); //$NON-NLS-1$
File[] profiles = readProfiles(dir);
if ( profiles.length == 0 ) {
File dirProfiles = new File(dir, "Profiles"); //$NON-NLS-1$
if ( !dirProfiles.exists() || !dirProfiles.isDirectory() ) {
dirProfiles = dir;
}
profiles = dirProfiles.listFiles( new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".default"); //$NON-NLS-1$
}
});
}
// Debug output
StringBuffer sb = new StringBuffer();
for( int j = 0; j < profiles.length; ++j ) {
if ( j != 0 ) {
sb.append(',');
}
sb.append(profiles[j].toString());
}
IdeLog.logInfo(JSDebugPlugin.getDefault(),StringUtils.format("Profiles found: {0}", sb.toString())); //$NON-NLS-1$
// End of Debug output
for( int j = 0; j < profiles.length; ++j ) {
File profile = profiles[j];
if ( profile.exists() && profile.isDirectory() ) {
IdeLog.logInfo(JSDebugPlugin.getDefault(),StringUtils.format("Default profile was found at {0}", profile.toString())); //$NON-NLS-1$
return profile;
}
}
}
}
return null;
}
/**
* installLinkedExtension
*
* @param extensionURL
* @param extensionID
* @param dir
* @return boolean
*/
public static boolean installLinkedExtension(URL extensionURL, String extensionID, File dir ) {
File file = new File(dir,extensionID);
if ( file.exists() && file.isDirectory() ) {
return true;
}
IPath base = JSDebugPlugin.getDefault().getStateLocation().addTrailingSeparator();
boolean result = installExtension(extensionURL,extensionID,base.toFile());
if ( result ) {
String linkedPath = base.append(extensionID).toOSString();
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
out.write(linkedPath.getBytes());
} catch (IOException e) {
IdeLog.logError(JSDebugPlugin.getDefault(),StringUtils.EMPTY,e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
}
return result;
}
/**
* installExtension
*
* @param extensionURL
* @param extensionID
* @param dir
* @return boolean
*/
public static boolean installExtension(URL extensionURL, String extensionID, File dir ) {
dir = new File(dir,extensionID);
if ( dir.exists() ) {
return true;
}
if ( !dir.mkdirs() ) {
return false;
}
File file = null;
InputStream in = null;
FileOutputStream out = null;
try {
file = File.createTempFile("ffe", ".zip"); //$NON-NLS-1$ //$NON-NLS-2$
in = extensionURL.openStream();
out = new FileOutputStream(file);
byte[] buffer = new byte[0x1000];
int n;
while ((n = in.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
IdeLog.logError(JSDebugPlugin.getDefault(),StringUtils.EMPTY,e);
if ( file != null ) {
file.delete();
}
return false;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
try {
extract( new ZipFile(file),dir);
} catch (IOException e) {
IdeLog.logError(JSDebugPlugin.getDefault(),StringUtils.EMPTY,e);
return false;
} finally {
file.delete();
}
return true;
}
/**
* Get extension version
* @param extensionID
* @param profileDir
* @return
*/
public static String getExtensionVersion(String extensionID, File profileDir) {
File dir = new File(new File(profileDir, "extensions"),extensionID); //$NON-NLS-1$
if ( dir.exists() ) {
File installRdf = new File(dir,"install.rdf"); //$NON-NLS-1$
if(installRdf.exists()) {
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = factory.newDocumentBuilder();
Document document = parser.parse(new FileInputStream(installRdf));
Node node = document.getDocumentElement().getFirstChild();
while(node != null) {
if("description".equals(node.getNodeName().toLowerCase()) //$NON-NLS-1$
|| "rdf:description".equals(node.getNodeName().toLowerCase())) { //$NON-NLS-1$
NamedNodeMap attrs = node.getAttributes();
Node about = attrs.getNamedItem("about"); //$NON-NLS-1$
if(about == null) {
about = attrs.getNamedItem("RDF:about"); //$NON-NLS-1$
}
if(about != null) {
if("urn:mozilla:install-manifest".equals(about.getNodeValue())) { //$NON-NLS-1$
break;
}
}
}
node = node.getNextSibling();
}
if(node != null) {
NamedNodeMap attrs = node.getAttributes();
Node version = attrs.getNamedItem("em:version"); //$NON-NLS-1$
if(version != null) {
return version.getNodeValue();
}
node = node.getFirstChild();
}
while(node != null) {
if("em:version".equals(node.getNodeName().toLowerCase())) { //$NON-NLS-1$
break;
}
node = node.getNextSibling();
}
if(node != null) {
return node.getTextContent();
}
}
catch (Exception e)
{
IdeLog.logError(JSDebugPlugin.getDefault(),StringUtils.EMPTY,e);
}
}
}
return null;
}
/**
* extract
*
* @param zip
* @param path
* @throws IOException
*/
private static void extract( ZipFile zip, File path ) throws IOException {
/* Create directories first */
for( Enumeration e = zip.entries(); e.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) e.nextElement();
String name = entry.getName();
File file = new File(path,name);
if ( entry.isDirectory() && !file.exists() ) {
file.mkdirs();
}
}
byte[] buffer = new byte[0x1000];
int n;
/* Extract files */
for( Enumeration e = zip.entries(); e.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) e.nextElement();
String name = entry.getName();
File file = new File(path,name);
if ( !entry.isDirectory() && !file.exists() ) {
if ( !file.createNewFile() )
{
continue;
}
OutputStream out = new FileOutputStream(file);
InputStream in = zip.getInputStream(entry);
while( (n = in.read(buffer)) > 0 ) {
out.write(buffer, 0, n);
}
in.close();
out.close();
}
}
}
/**
* readProfiles
*
* @param file
* @return File[]
*/
private static File[] readProfiles(File dir) {
List<File> list = new ArrayList<File>();
File profilesIni = new File(dir,"profiles.ini"); //$NON-NLS-1$
if ( profilesIni.exists() ) {
LineNumberReader r = null;
try {
r = new LineNumberReader(new FileReader(profilesIni));
String line;
Map<String, Map<String, String>> sections = new HashMap<String, Map<String, String>>();
Map<String, String> last = null;
Pattern sectionPattern = Pattern.compile("^\\x5B(.*)\\x5D$"); //$NON-NLS-1$
Pattern valuePattern = Pattern.compile("^(.[^=]*)=(.*)$"); //$NON-NLS-1$
while ((line = r.readLine()) != null) {
Matcher matcher = sectionPattern.matcher(line);
if ( matcher.find() ) {
last = new HashMap<String, String>();
sections.put(matcher.group(1),last);
continue;
} else if ( last == null ) {
continue;
}
matcher = valuePattern.matcher(line);
if ( matcher.find() ) {
last.put(matcher.group(1),matcher.group(2));
}
}
for( Iterator i = sections.keySet().iterator(); i.hasNext(); ) {
String section = (String) i.next();
if ( section.startsWith("Profile") ) { //$NON-NLS-1$
Map properties = (Map) sections.get(section);
String path = (String) properties.get("Path"); //$NON-NLS-1$
String isRelative = (String) properties.get("IsRelative"); //$NON-NLS-1$
File profile;
if ( isRelative != null && "1".equals(isRelative) ) { //$NON-NLS-1$
profile = new File(dir, path);
} else {
profile = new File(path); // TODO: base64 decode ?
}
boolean def = properties.containsKey("Default"); //$NON-NLS-1$
if ( def ) {
list.add(0,profile);
} else {
list.add(profile);
}
}
}
} catch (IOException e) {
IdeLog.logError(JSDebugPlugin.getDefault(),
StringUtils.format("Reading '{0}' fails", profilesIni.getAbsolutePath()), e); //$NON-NLS-1$
} finally {
if (r != null) {
try {
r.close();
} catch (IOException ignore) {
}
}
}
}
return (File[]) list.toArray(new File[list.size()]);
}
}