/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.foundation.ui.credentials.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image;
import org.jboss.tools.foundation.core.ecf.URLTransportUtility;
import org.jboss.tools.foundation.ui.internal.FoundationUIPlugin;
import org.jboss.tools.foundation.ui.plugin.BaseUISharedImages;
import org.jboss.tools.magicfile4j.IMagicFileModel;
import org.jboss.tools.magicfile4j.MagicFileLoader;
import org.jboss.tools.magicfile4j.MagicResult;
import org.jboss.tools.magicfile4j.MagicRunner;
import org.osgi.framework.Bundle;
/**
* A cache for downloaded favicon.ico files for given hosts.
* The images are stored in a {host} -> {host}.ico format in the map,
* as well as in metadata locations.
* All requests for images should be made using the host alone as the key.
*/
public class FaviconCache extends BaseUISharedImages {
private static final FaviconCache instance = new FaviconCache(FoundationUIPlugin.getDefault().getBundle());
public static FaviconCache getDefault() {
return instance;
}
// A folder in state location to hold favicons
private static final String FAVICONS = "favicons";
private static final String DOT_ICO = ".ico";
private static final String[] allowedFormats = new String[]{
"image/vnd.microsoft.icon",
"image/x-icon",
"image/png",
"image/gif",
"image/jpeg",
"image/svg+xml"
};
private Set<String> failedLoads = new HashSet<String>();
public FaviconCache(Bundle pluginBundle) {
super(pluginBundle);
init();
}
private void init() {
// Load all already-downloaded icons
IPath p = FoundationUIPlugin.getDefault().getStateLocation().append(FAVICONS);
if( p.toFile().exists() ) {
File[] all = p.toFile().listFiles();
for( int i = 0; i < all.length; i++ ) {
if( all[i].getName().endsWith(DOT_ICO)) {
String host = all[i].getName().substring(0, all[i].getName().lastIndexOf("."));
addImage(host, all[i].getName());
}
}
} else {
p.toFile().mkdirs();
}
}
/**
* An interface to be alerted if a new favicon has been downloaded
*/
public static interface FaviconCacheListener {
public void iconCached(String host);
public void fetchFailed(String host);
}
/**
* An asynchronous method to load the favicon for a given host,
* and alert a listener when complete
* @param host
* @param listener
*/
public void loadFavicon(final String host, final FaviconCacheListener listener) {
new Thread("Load favicon.ico for " + host) {
public void run() {
loadFaviconSync(host, listener);
}
}.start();
}
private void loadFaviconSync(String host, FaviconCacheListener listener) {
File f = fetchFaviconSync(host, listener);
if( f == null ) {
failedLoads.add(host);
listener.fetchFailed(host);
} else {
String fileType = getMimeType(f);
if( fileType == null || !Arrays.asList(allowedFormats).contains(fileType)) {
// unapproved file type
failedLoads.add(host);
listener.fetchFailed(host);
} else {
// copy the file, and add it
IPath p = FoundationUIPlugin.getDefault().getStateLocation().append(FAVICONS);
String target = host+DOT_ICO;
File targetFile = p.append(target).toFile();
if( targetFile.exists()) {
targetFile.delete();
}
boolean success = f.renameTo(targetFile);
if( success ) {
addImage(host,target);
listener.iconCached(host);
} else {
failedLoads.add(host);
listener.fetchFailed(host);
}
}
}
}
private String getMimeType(File f) {
if( f == null || !f.exists())
return null;
IMagicFileModel mod = getMagicModel();
try {
MagicResult result = new MagicRunner(mod).runMatcher(f);
if( result != null ) {
return result.getMimeType();
}
} catch(IOException ioe) {
FoundationUIPlugin.pluginLog().logError("Error reading file type for file " + f.getName(), ioe);
}
return null;
}
private IMagicFileModel magicModel = null;
private boolean magicLoadFailed = false;
private IMagicFileModel getMagicModel() {
if( magicModel == null && !magicLoadFailed) {
URL url = null;
InputStream inputStream = null;
IMagicFileModel model = null;
try {
url = new URL("platform:/plugin/org.jboss.tools.foundation.ui/resources/images.magic");
inputStream = url.openConnection().getInputStream();
model = new MagicFileLoader().readMagicFile(inputStream);
magicModel = model;
} catch (MalformedURLException e) {
magicLoadFailed = true;
FoundationUIPlugin.pluginLog().logError("Error loading magic file for verifying image types", e);
} catch(IOException ioe) {
magicLoadFailed = true;
FoundationUIPlugin.pluginLog().logError("Error loading magic file for verifying image types", ioe);
} finally {
if (inputStream != null ) {
try {
inputStream.close();
} catch(IOException ioe2) {
// ignore
}
}
}
}
return magicModel;
}
public boolean loadFailed(String host) {
return failedLoads.contains(host);
}
private File fetchFaviconSync(String host, FaviconCacheListener listener) {
try {
String url = "http://" + host + "/favicon.ico";
File f = new URLTransportUtility().getCachedFileForURL(url, "Fetching favicon for " + host, URLTransportUtility.CACHE_UNTIL_EXIT, new NullProgressMonitor());
if( f != null && f.length() > 0)
return f;
} catch (CoreException ce) {
FoundationUIPlugin.pluginLog().logError(ce);
}
return null;
}
public ImageDescriptor getDescriptorForHost(String host) {
return descriptor(host);
}
public Image getImageForHost(String host) {
try {
return image(host);
} catch(SWTException swte) {
// ignore, just return no image
}
return null;
}
protected ImageDescriptor createImageDescriptor(String path) {
try {
IPath p = FoundationUIPlugin.getDefault().getStateLocation().append(FAVICONS);
p = p.append(path);
return ImageDescriptor.createFromURL(p.toFile().toURI().toURL());
} catch(MalformedURLException murle) {
FoundationUIPlugin.pluginLog().logError("Error generating favicon image descriptor for host " + path);
}
return null;
}
public static void cleanup() {
FaviconCache.instance.dispose();
}
}