| /******************************************************************************* |
| * 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; |
| } |
| |
| } |