blob: 4fee8b204082f7ba82873e3154e8d23eadc148ee [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.help.internal.protocols;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.help.internal.base.remote.HttpsUtility;
import org.eclipse.help.internal.base.remote.PreferenceFileHandler;
import org.eclipse.help.internal.base.remote.RemoteContentLocator;
import org.eclipse.help.internal.base.remote.RemoteHelp;
import org.eclipse.help.internal.base.remote.RemoteHelpInputStream;
import org.eclipse.help.internal.base.util.ProxyUtil;
import org.eclipse.help.internal.util.ResourceLocator;
import org.eclipse.help.internal.util.URLCoder;
import org.osgi.framework.Bundle;
/**
* URLConnection to help documents in plug-ins
*/
public class HelpURLConnection extends URLConnection {
private final static String PARAM_LANG = "lang"; //$NON-NLS-1$
private final static String PRODUCT_PLUGIN = "PRODUCT_PLUGIN"; //$NON-NLS-1$
public final static String PLUGINS_ROOT = "PLUGINS_ROOT/"; //$NON-NLS-1$
private final static String PATH_RTOPIC = "/rtopic"; //$NON-NLS-1$
private static final String PROTOCOL_HTTP = "http://"; //$NON-NLS-1$
private static Hashtable<String, String[]> templates = new Hashtable<String, String[]>();
// document caching - disabled if running in dev mode
protected static boolean cachingEnabled = true;
static {
String[] args = Platform.getCommandLineArgs();
for (int i = 0; i < args.length; i++) {
if ("-dev".equals(args[i])) { //$NON-NLS-1$
cachingEnabled = false;
break;
}
}
}
protected String pluginAndFile; // plugin/file
protected String query; // after ?
protected HashMap<String, Object> arguments;
protected Bundle plugin;
// file in a plug-in
protected String file;
protected String locale;
private static String appserverImplPluginId;
private boolean localOnly;
/**
* Constructor for HelpURLConnection
*/
public HelpURLConnection(URL url) {
this(url, false);
}
public HelpURLConnection(URL url, boolean localOnly) {
super(url);
this.localOnly = localOnly;
String urlFile = url.getFile();
// Strip off everything before and including the PLUGINS_ROOT
int index = urlFile.indexOf(PLUGINS_ROOT);
if (index != -1)
urlFile = urlFile.substring(index + PLUGINS_ROOT.length());
// Strip off the leading "/" and the query
if (urlFile.startsWith("/")) //$NON-NLS-1$
urlFile = urlFile.substring(1);
int indx = urlFile.indexOf("?"); //$NON-NLS-1$
if (indx != -1) {
query = urlFile.substring(indx + 1);
urlFile = urlFile.substring(0, indx);
}
this.pluginAndFile = urlFile;
parseQuery();
setDefaultUseCaches(isCacheable());
}
/**
* @see URLConnection#connect()
*/
public void connect() throws IOException {
}
/**
* see URLConnection#getInputStream(); Note: this method can throw IOException, but should never
* return null
*/
public InputStream getInputStream() throws IOException {
// must override parent implementation, since it does nothing.
Bundle plugin = getPlugin();
if (plugin != null && plugin.getSymbolicName().equals(getAppserverImplPluginId())) {
// Do not return documents from app server implementation plug-in
throw new IOException("Resource not found."); //$NON-NLS-1$
}
if (getFile() == null || "".equals(getFile()) || getFile().indexOf("..\\") >= 0) { //$NON-NLS-1$ //$NON-NLS-2$
throw new IOException("Resource not found."); //$NON-NLS-1$
}
int helpOption=localOnly ? PreferenceFileHandler.LOCAL_HELP_ONLY
: PreferenceFileHandler.getEmbeddedHelpOption();
InputStream in = null;
if (plugin != null && (helpOption==PreferenceFileHandler.LOCAL_HELP_ONLY || helpOption==PreferenceFileHandler.LOCAL_HELP_PRIORITY)) {
in = getLocalHelp(plugin);
}
if (in == null && (helpOption==PreferenceFileHandler.LOCAL_HELP_PRIORITY || helpOption==PreferenceFileHandler.REMOTE_HELP_PRIORITY)) {
in = openFromRemoteServer(getHref(), getLocale());
if( in != null ){
in = new RemoteHelpInputStream(in);
}
if(in==null && plugin!=null && helpOption==PreferenceFileHandler.REMOTE_HELP_PRIORITY)
{
in = getLocalHelp(plugin);
}
}
if (in == null) {
throw new IOException("Resource not found."); //$NON-NLS-1$
}
return in;
}
private InputStream getLocalHelp(Bundle plugin) {
InputStream in;
// first try using content provider, then try to find the file
// inside doc.zip, and finally try the file system
in = ResourceLocator.openFromProducer(plugin,
query == null ? getFile() : getFile() + "?" + query, //$NON-NLS-1$
getLocale());
if (in == null) {
in = ResourceLocator.openFromPlugin(plugin, getFile(), getLocale());
}
if (in == null) {
in = ResourceLocator.openFromZip(plugin, "doc.zip", //$NON-NLS-1$
getFile(), getLocale());
}
return in;
}
public long getExpiration() {
return isCacheable() ? new Date().getTime() + 10000 : 0;
}
public static void parseQuery(String query, HashMap<String, Object> arguments) {
StringTokenizer stok = new StringTokenizer(query, "&"); //$NON-NLS-1$
while (stok.hasMoreTokens()) {
String aQuery = stok.nextToken();
int equalsPosition = aQuery.indexOf("="); //$NON-NLS-1$
if (equalsPosition > -1) { // well formed name/value pair
String arg = aQuery.substring(0, equalsPosition);
String val = aQuery.substring(equalsPosition + 1);
Object existing = arguments.get(arg);
if (existing == null)
arguments.put(arg, val);
else if (existing instanceof Vector) {
@SuppressWarnings("unchecked")
Vector<String> vector = (Vector<String>) existing;
vector.add(val);
arguments.put(arg, existing);
} else {
Vector<Object> v = new Vector<Object>(2);
v.add(existing);
v.add(val);
arguments.put(arg, v);
}
}
}
}
/**
* NOTE: need to add support for multi-valued parameters (like filtering) Multiple values are
* added as vectors
*/
protected void parseQuery() {
if (query != null && !"".equals(query)) { //$NON-NLS-1$
if (arguments == null) {
arguments = new HashMap<String, Object>(5);
}
parseQuery(query, arguments);
}
}
public String getContentType() {
// Check if the file is hypertext or plain text
String file = pluginAndFile.toLowerCase(Locale.US);
if (file.endsWith(".html") || file.endsWith(".htm") //$NON-NLS-1$ //$NON-NLS-2$
|| file.endsWith(".xhtml")) //$NON-NLS-1$
return "text/html"; //$NON-NLS-1$
else if (file.endsWith(".css")) //$NON-NLS-1$
return "text/css"; //$NON-NLS-1$
else if (file.endsWith(".gif")) //$NON-NLS-1$
return "image/gif"; //$NON-NLS-1$
else if (file.endsWith(".jpg")) //$NON-NLS-1$
return "image/jpeg"; //$NON-NLS-1$
else if (file.endsWith(".pdf")) //$NON-NLS-1$
return "application/pdf"; //$NON-NLS-1$
else if (file.endsWith(".xml")) //$NON-NLS-1$
return "application/xml"; //$NON-NLS-1$
else if (file.endsWith(".xsl")) //$NON-NLS-1$
return "application/xsl"; //$NON-NLS-1$
return "text/plain"; //$NON-NLS-1$
}
/**
*
*/
public Vector<String> getMultiValue(String name) {
if (arguments != null) {
Object value = arguments.get(name);
if (value instanceof Vector) {
@SuppressWarnings("unchecked")
Vector<String> vector = (Vector<String>) value;
return vector;
}
return null;
}
return null;
}
/**
*
*/
public String getValue(String name) {
if (arguments == null)
return null;
Object value = arguments.get(name);
String stringValue = null;
if (value instanceof String)
stringValue = (String) value;
else if (value instanceof Vector) {
@SuppressWarnings("unchecked")
Vector<String> vector = (Vector<String>) value;
stringValue = vector.firstElement();
} else
return null;
try {
return URLCoder.decode(stringValue);
} catch (Exception e) {
return null;
}
}
/**
* Returns the locale specified by client.
*/
protected String getLocale() {
if (locale == null) {
locale = getValue(PARAM_LANG);
if (locale == null) {
locale = Platform.getNL();
}
}
return locale;
}
protected String getFile() {
if (file == null) {
// Strip the plugin id
int start = pluginAndFile.indexOf("/") + 1; //$NON-NLS-1$
// Strip query string or anchor bookmark
int end = pluginAndFile.indexOf("?"); //$NON-NLS-1$
if (end == -1)
end = pluginAndFile.indexOf("#"); //$NON-NLS-1$
if (end == -1)
end = pluginAndFile.length();
file = pluginAndFile.substring(start, end);
file = URLCoder.decode(file);
}
return file;
}
protected Bundle getPlugin() {
if (plugin == null) {
// Assume the url is pluginID/path_to_topic.html
int i = pluginAndFile.indexOf('/');
String pluginId = i == -1 ? "" : pluginAndFile.substring(0, i); //$NON-NLS-1$
pluginId = URLCoder.decode(pluginId);
if (PRODUCT_PLUGIN.equals(pluginId)) {
IProduct product = Platform.getProduct();
if (product != null) {
plugin = product.getDefiningBundle();
return plugin;
}
}
plugin = Platform.getBundle(pluginId);
}
return plugin;
}
private String getHref() {
return '/' + pluginAndFile;
}
public boolean isCacheable() {
if (getValue("resultof") != null) //$NON-NLS-1$
return false;
return cachingEnabled;
}
public String toString() {
return pluginAndFile;
}
/**
* Obtains ID of plugin that contributes appserver implementation. *
*
* @return plug-in ID or null
*/
private static String getAppserverImplPluginId() {
if (appserverImplPluginId == null) {
// This part mimics AppserverPlugin.createWebappServer()
// get the app server extension from the system plugin registry
IExtensionRegistry pluginRegistry = Platform.getExtensionRegistry();
IExtensionPoint point = pluginRegistry.getExtensionPoint("org.eclipse.help.appserver.server"); //$NON-NLS-1$
if (point != null) {
IExtension[] extensions = point.getExtensions();
if (extensions.length != 0) {
// We need to pick up the non-default configuration
IConfigurationElement[] elements = extensions[0].getConfigurationElements();
if (elements.length == 0)
return null;
IConfigurationElement serverElement = null;
for (int i = 0; i < elements.length; i++) {
String defaultValue = elements[i].getAttribute("default"); //$NON-NLS-1$
if (defaultValue == null || defaultValue.equals("false")) { //$NON-NLS-1$
serverElement = elements[i];
break;
}
}
// if all the servers are default, then pick the first one
if (serverElement == null) {
serverElement = elements[0];
}
//
appserverImplPluginId = serverElement.getContributor().getName();
}
}
}
return appserverImplPluginId;
}
/*
* Opens a connection to the document on the remote help server, if one was specified. If the
* document doesn't exist on the remote server, returns null.
*/
private InputStream openFromRemoteServer(String href, String locale) {
if (RemoteHelp.isEnabled()) {
String pathSuffix = PATH_RTOPIC + href + '?' + PARAM_LANG + '=' + locale;
/*
* Get the URL that maps to the contributorID Assume the url is
* pluginID/path_to_topic.html
*/
int i = pluginAndFile.indexOf('/');
String pluginId = i == -1 ? "" : pluginAndFile.substring(0, i); //$NON-NLS-1$
pluginId = URLCoder.decode(pluginId);
String remoteURL = RemoteContentLocator.getUrlForContent(pluginId);
InputStream in;
if (remoteURL == null) {
in = tryOpeningAllServers(pathSuffix);
} else {
in = openRemoteStream(remoteURL, pathSuffix);
}
return in;
}
return null;
}
private InputStream getUnverifiedStream(String remoteURL,String pathSuffix)
{
URL url;
InputStream in = null;
try {
if(remoteURL.startsWith(PROTOCOL_HTTP))
{
url = new URL(remoteURL + pathSuffix);
HttpURLConnection connection = (HttpURLConnection) ProxyUtil.getConnection(url);
in = connection.getInputStream();
}
else
{
url = HttpsUtility.getHttpsURL(remoteURL + pathSuffix);
in = HttpsUtility.getHttpsStream(url);
}
} catch (Exception e) {
// File not found on this server
}
return in;
}
private InputStream openRemoteStream(String remoteURL, String pathSuffix) {
InputStream in = getUnverifiedStream(remoteURL,pathSuffix);
String errPage[] = templates.get(remoteURL);
if (errPage==null)
{
String error = getPageText(getUnverifiedStream(remoteURL,"/rtopic/fakeurltogetatestpage/_ACEGIKMOQ246.html")); //$NON-NLS-1$
if (error!=null)
{
errPage = error.split("\n"); //$NON-NLS-1$
templates.put(remoteURL,errPage);
}
else
{
errPage = new String[0];
templates.put(remoteURL,errPage);
}
}
// No error page, InfoCenter is at least 3.6, so it is
// returning null already.
if (errPage.length==0)
return in;
// Check to see if the URL is the error page for the
// remote IC. If so, return null.
if (compare(errPage,getUnverifiedStream(remoteURL,pathSuffix)))
{
try{
in.close();
}catch(Exception ex){}
return null;
}
return in;
}
private boolean compare(String lines[],InputStream in)
{
try{
if (in!=null)
{
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
int count = 0;
while ((line = br.readLine())!=null)
{
if (count>lines.length)
return false;
if (!lines[count].equals(line))
return false;
count++;
}
br.close();
in.close();
return true;
}
}catch(Exception ex)
{}
return false;
}
private String getPageText(InputStream in) {
try{
if (in!=null)
{
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line,result=""; //$NON-NLS-1$
while ((line = br.readLine())!=null)
{
result+=line+'\n';
}
br.close();
in.close();
return result;
}
}catch(Exception ex){}
return null;
}
private InputStream tryOpeningAllServers(String pathSuffix) {
PreferenceFileHandler prefHandler = new PreferenceFileHandler();
String host[] = prefHandler.getHostEntries();
String port[] = prefHandler.getPortEntries();
String protocol[] = prefHandler.getProtocolEntries();
String path[] = prefHandler.getPathEntries();
String isEnabled[] = prefHandler.isEnabled();
int numICs = host.length;
for (int i = 0; i < numICs; i++) {
if (isEnabled[i].equalsIgnoreCase("true")) { //$NON-NLS-1$
String urlStr = protocol[i]+"://" + host[i] + ':' + port[i] + path[i]; //$NON-NLS-1$
InputStream is = openRemoteStream(urlStr, pathSuffix);
if (is != null) {
return is;
}
}
}
return null;
}
}