package org.sigmah.server.servlet; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.net.URISyntaxException; import java.net.URL; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.sigmah.linker.Manifest; import org.sigmah.server.inject.ServletModule; import org.sigmah.server.util.Languages; import org.sigmah.shared.Language; import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Parse the user-agent to determine which manifest is the best match for the * user. * * @author Raphaƫl Calabro (rcalabro@ideia.fr) */ @Singleton public class ManifestServlet extends HttpServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ManifestServlet.class); private static final String PATH_OF_THIS_CLASS; private static final String USER_AGENT_HEADER = "user-agent"; private static final String MANIFEST_MIME_TYPE = "text/cache-manifest"; private static final String MANIFEST_EXTENSION = ".manifest"; private static final String ENCODING = "UTF-8"; static { final String name = ManifestServlet.class.getName(); PATH_OF_THIS_CLASS = '/' + name.replace('.', '/') + ".class"; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final String userAgent = req.getHeader(USER_AGENT_HEADER); final Language language = Languages.getLanguage(req); final String permutation = getPermutation(userAgent.toLowerCase()); final Manifest manifest = new Manifest(permutation, language); LOGGER.info("Requesting HTML5 manifest for " + manifest); resp.setContentType(MANIFEST_MIME_TYPE); resp.setCharacterEncoding(ENCODING); File manifestFile = null; try { final URL resourceUrl = ManifestServlet.class.getResource(PATH_OF_THIS_CLASS); final File gwtXmlFile = new File(resourceUrl.toURI()); final File rootFolder = gwtXmlFile // ManifestServlet.class .getParentFile() // servlet .getParentFile() // server .getParentFile() // sigmah .getParentFile() // org .getParentFile() // classes .getParentFile() // WEB-INF .getParentFile(); // root final File jsFolder = new File(rootFolder, ServletModule.ENDPOINT); manifestFile = new File(jsFolder, manifest.toFileName()); if(!manifestFile.exists()) { final Manifest defaultLocaleAlternative = new Manifest(permutation, (String)null); manifestFile = new File(jsFolder, defaultLocaleAlternative.toFileName()); if(!manifestFile.exists()) { final Manifest defaultUserAgentAlternative = new Manifest(null, language); manifestFile = new File(jsFolder, defaultUserAgentAlternative.toFileName()); if(!manifestFile.exists()) { final Manifest defaultAlternative = new Manifest(); manifestFile = new File(jsFolder, defaultAlternative.toFileName()); } } } } catch (URISyntaxException ex) { LOGGER.error("The path to this class ('" + PATH_OF_THIS_CLASS + "') could not be transformed to a valid URI.", ex); } if(manifestFile == null || !manifestFile.exists()) { throw new FileNotFoundException(permutation + MANIFEST_EXTENSION); } try (FileInputStream inputStream = new FileInputStream(manifestFile); OutputStream outputStream = resp.getOutputStream()) { final byte[] cache = new byte[1024]; int length = inputStream.read(cache); while(length != -1) { outputStream.write(cache, 0, length); length = inputStream.read(cache); } } } /** * This method is based on the one used by GWT when choosing which * permutation to use. * * @param userAgent User-agent of the web browser. * @return Permutation associated with the given browser. * @throws UnsupportedOperationException If the given web browser is not supported. */ private String getPermutation(String userAgent) { if (userAgent.contains("opera")) { return "opera"; } else if (userAgent.contains("webkit")) { return "safari"; } else if (userAgent.contains("trident/")) { final int versionStart = userAgent.indexOf("trident/"); final int versionEnd = userAgent.indexOf('.', versionStart); final int version = Integer.parseInt(userAgent.substring(versionStart + "trident/".length(), versionEnd)); if(version < 4) { return "ie6"; } else if(version == 4) { return "ie8"; } else { return "ie9"; } } else if (userAgent.contains("gecko")) { return "gecko1_8"; } throw new UnsupportedOperationException("Web browser is unsupported: " + userAgent); } }