/** * RemoteAccess * Copyright 22.02.2015 by Michael Peter Christen, @0rb1t3r * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program in the file lgpl21.txt * If not, see <http://www.gnu.org/licenses/>. */ package org.loklak.http; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.URLDecoder; import java.net.UnknownHostException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import org.elasticsearch.common.Base64; import org.loklak.data.DAO; import org.loklak.graphics.RasterPlotter; import org.loklak.server.Query; import org.loklak.tools.UTF8; /** * Storage of a peer list which can be used for peer-to-peer communication. * This is a static class to provide access to all other objects easily. * We store the IPs here only temporary, not permanently. */ public class RemoteAccess { public static Map<String, Map<String, RemoteAccess>> history = new ConcurrentHashMap<String, Map<String, RemoteAccess>>(); public static Query evaluate(final HttpServletRequest request) { try{ request.setCharacterEncoding("UTF-8");} catch (UnsupportedEncodingException e){} // set character encoding before any request is made String path = request.getServletPath(); Map<String, String> qm = getQueryMap(request.getQueryString()); Query post = new Query(request); post.initGET(qm); String httpports = qm == null ? request.getParameter("port.http") : qm.get("port.http"); Integer httpport = httpports == null ? null : Integer.parseInt(httpports); String httpsports = qm == null ? request.getParameter("port.https") : qm.get("port.https"); Integer httpsport = httpsports == null ? null : Integer.parseInt(httpsports); String peername = qm == null ? request.getParameter("peername") : qm.get("peername"); if (peername == null || peername.length() > 132) peername = "anonymous"; final String remoteHost = post.getClientHost(); Map<String, RemoteAccess> hmap = history.get(request.getServletPath()); if (hmap == null) {hmap = new ConcurrentHashMap<>(); history.put(request.getServletPath(), hmap);} if (httpport == null || httpsport == null) { // if port configuration is omitted, just update the value if it exist RemoteAccess ra = hmap.get(remoteHost); if (ra == null) { hmap.put(remoteHost, new RemoteAccess(remoteHost, path, httpport, httpsport, peername)); } else { assert ra.remoteHost.equals(remoteHost); ra.localPath = path; ra.accessTime = System.currentTimeMillis(); } } else { // overwrite if new port configuration is submitted RemoteAccess ra = new RemoteAccess(remoteHost, path, httpport, httpsport, peername); hmap.put(remoteHost, ra); DAO.updateFrontPeerCache(ra); } return post; } public static long latestVisit(String servlet, String remoteHost) { Map<String, RemoteAccess> hmap = history.get(servlet); if (hmap == null) {hmap = new ConcurrentHashMap<>(); history.put(servlet, hmap);} RemoteAccess ra = hmap.get(remoteHost); return ra == null ? -1 : ra.accessTime; } public static String hostHash(String remoteHost) { return Integer.toHexString(Math.abs(remoteHost.hashCode())); } private String remoteHost, localPath, peername; private int localHTTPPort, localHTTPSPort; private long accessTime; private RemoteAccess(final String remoteHost, final String localPath, final Integer localHTTPPort, final Integer localHTTPSPort, final String peername) { this.remoteHost = remoteHost; this.localPath = localPath; this.localHTTPPort = localHTTPPort == null ? -1 : localHTTPPort.intValue(); this.localHTTPSPort = localHTTPSPort == null ? -1 : localHTTPSPort.intValue(); this.peername = peername; this.accessTime = System.currentTimeMillis(); } public String getRemoteHost() { return this.remoteHost; } public String getLocalPath() { return this.localPath; } public long getAccessTime() { return this.accessTime; } public int getLocalHTTPPort() { return this.localHTTPPort; } public int getLocalHTTPSPort() { return this.localHTTPSPort; } public String getPeername() { return this.peername; } private static Set<String> localhostNames = new HashSet<>(); private static Set<Pattern> localhostReferrer = new HashSet<>(); static { localhostNames.add("0:0:0:0:0:0:0:1"); localhostNames.add("fe80:0:0:0:0:0:0:1%1"); localhostNames.add("127.0.0.1"); localhostNames.add("localhost"); try {localhostNames.add(InetAddress.getLocalHost().getHostAddress());} catch (UnknownHostException e) {} try {localhostNames.add(InetAddress.getLocalHost().getHostName());} catch (UnknownHostException e) {} try {localhostNames.add(InetAddress.getLocalHost().getCanonicalHostName());} catch (UnknownHostException e) {} try {for (InetAddress a: InetAddress.getAllByName(null)) {localhostNames.add(a.getHostAddress()); localhostNames.add(a.getHostName()); localhostNames.add(a.getCanonicalHostName());}} catch (UnknownHostException e) {} try {for (InetAddress a: InetAddress.getAllByName("localhost")) {localhostNames.add(a.getHostAddress()); localhostNames.add(a.getHostName()); localhostNames.add(a.getCanonicalHostName());}} catch (UnknownHostException e) {} //System.out.println(localhostNames); } public static void addLocalhost(String h) { localhostNames.add(h); } public static void addReferrer(Pattern p) { localhostReferrer.add(p); } public static boolean isLocalhost(String host, String referrer) { if (localhostNames.contains(host)) return true; for (Pattern p: localhostReferrer) { if (p.matcher(referrer).find()) return true; } return false; } public static Map<String, String> getQueryMap(String query) { Map<String, String> map = new HashMap<String, String>(); if (query == null) return map; String[] params = query.split("&"); for (String param : params) { int p = param.indexOf('='); if (p >= 0) try { map.put(param.substring(0, p), URLDecoder.decode(param.substring(p + 1), "UTF-8")); } catch (UnsupportedEncodingException e) {} } return map; } public static Map<String, byte[]> getPostMap(HttpServletRequest request) { Map<String, byte[]> map = new HashMap<>(); Map<String, String[]> pm = request.getParameterMap(); if (pm != null && pm.size() > 0) { for (Map.Entry<String, String[]> entry: pm.entrySet()) { String[] v = entry.getValue(); if (v != null && v.length > 0) map.put(entry.getKey(), UTF8.getBytes(v[0])); } } else try { final byte[] b = new byte[1024]; for (Part part: request.getParts()) { String name = part.getName(); InputStream is = part.getInputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); int c; try {while ((c = is.read(b, 0, b.length)) > 0) { baos.write(b, 0, c); }} finally {is.close();} map.put(name, baos.toByteArray()); } } catch (IOException e) { } catch (ServletException e) { } return map; } public static enum FileType { UNKNOWN, PNG, JPG, GIF, JSON, RSS; } public static class FileTypeEncoding { public final FileType fileType; public final boolean base64; private FileTypeEncoding(final FileType fileType, final boolean base64) { this.fileType = fileType; this.base64 = base64; } private FileTypeEncoding(final FileType fileType) { this.fileType = fileType; this.base64 = false; } } public static FileTypeEncoding getFileType(HttpServletRequest request) { String servletPath = request.getServletPath(); if (servletPath.endsWith(".gif")) return new FileTypeEncoding(FileType.GIF); if (servletPath.endsWith(".gif.base64")) return new FileTypeEncoding(FileType.GIF, true); if (servletPath.endsWith(".png")) return new FileTypeEncoding(FileType.PNG); if (servletPath.endsWith(".png.base64")) return new FileTypeEncoding(FileType.PNG, true); if (servletPath.endsWith(".jpg")) return new FileTypeEncoding(FileType.JPG); if (servletPath.endsWith(".jpg.base64")) return new FileTypeEncoding(FileType.JPG, true); return new FileTypeEncoding(FileType.UNKNOWN); } public static void writeImage(final FileTypeEncoding fileType, final HttpServletResponse response, Query post, final RasterPlotter matrix) throws IOException { // write image ServletOutputStream sos = response.getOutputStream(); if (fileType.fileType == FileType.PNG) { post.setResponse(response, fileType.base64 ? "application/octet-stream" : "image/png"); sos.write(fileType.base64 ? Base64.encodeBytes(matrix.pngEncode(1)).getBytes() : matrix.pngEncode(1)); } if (fileType.fileType == FileType.GIF) { post.setResponse(response, fileType.base64 ? "application/octet-stream" : "image/gif"); if (fileType.base64) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(matrix.getImage(), "gif", baos); baos.close(); sos.write(Base64.encodeBytes(baos.toByteArray()).getBytes()); } else { ImageIO.write(matrix.getImage(), "gif", sos); } } if (fileType.fileType == FileType.JPG) { post.setResponse(response, fileType.base64 ? "application/octet-stream" : "image/jpeg"); if (fileType.base64) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(matrix.getImage(), "jpg", baos); baos.close(); sos.write(Base64.encodeBytes(baos.toByteArray()).getBytes()); } else { ImageIO.write(matrix.getImage(), "jpg", sos); } } } }