package org.lobobrowser.security; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.HierarchyBoundsListener; import java.awt.event.HierarchyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import org.lobobrowser.security.PermissionSystem.Permission; import org.lobobrowser.security.PermissionSystem.PermissionBoard.PermissionRow; import org.lobobrowser.ua.NavigationEntry; import org.lobobrowser.ua.NavigatorFrame; import org.lobobrowser.ua.UserAgentContext; import org.lobobrowser.ua.UserAgentContext.Request; import org.lobobrowser.ua.UserAgentContext.RequestKind; import org.lobobrowser.util.gui.GUITasks; public final class RequestManager { private static final Logger logger = Logger.getLogger(RequestManager.class.getName()); private final NavigatorFrame frame; public RequestManager(final NavigatorFrame frame) { this.frame = frame; } private static class RequestCounters { private final int counters[] = new int[UserAgentContext.RequestKind.values().length]; public void updateCounts(final RequestKind kind) { counters[kind.ordinal()]++; } @Override public String toString() { return Arrays.stream(RequestKind.values()) .map(kind -> String.format(" %2d", counters[kind.ordinal()])).reduce((e, a) -> e + a) .orElse(""); } } private Map<String, RequestCounters> hostToCounterMap = new HashMap<>(); private Optional<PermissionSystem> permissionSystemOpt = Optional.empty(); private synchronized void updateCounter(final Request request) { final String host = request.url.getHost().toLowerCase(); ensureHostInCounter(host); hostToCounterMap.get(host).updateCounts(request.kind); } private void ensureHostInCounter(final String host) { if (!hostToCounterMap.containsKey(host)) { hostToCounterMap.put(host, new RequestCounters()); } } private Optional<NavigationEntry> getFrameNavigationEntry() { final NavigationEntry currentNavigationEntry = frame.getCurrentNavigationEntry(); return Optional.ofNullable(currentNavigationEntry); } private Optional<String> getFrameHost() { return getFrameNavigationEntry().map(e -> { final String host = e.getUrl().getHost(); return host == null ? "" : host.toLowerCase(); }); } private Optional<URL> getFrameURL() { return getFrameNavigationEntry().map(e -> e.getUrl()); } private Request rewriteRequest(final Request request) { final Optional<String> frameHostOpt = getFrameHost(); if (request.url.getProtocol().equals("data") && frameHostOpt.isPresent()) { try { return new Request(new URL("data", frameHostOpt.get(), "someDataPath"), request.kind); } catch (final MalformedURLException e) { throw new RuntimeException("Couldn't rewrite data request"); } } else { return request; } } public boolean isRequestPermitted(final Request request) { final Request finalRequest = rewriteRequest(request); if (permissionSystemOpt.isPresent()) { final Boolean permitted = permissionSystemOpt.map(p -> p.isRequestPermitted(finalRequest)).orElse(false); updateCounter(finalRequest); // dumpCounters(); return permitted; } else { logger.severe("Unexpected permission system state. Request without context!"); return false; } } private void setupPermissionSystem(final String frameHost) { final RequestRuleStore permissionStore = RequestRuleStore.getStore(); final PermissionSystem system = new PermissionSystem(frameHost, permissionStore); // Prime the boards with atleast one row system.getLastBoard().getRow(frameHost); permissionSystemOpt = Optional.of(system); } @SuppressWarnings("unused") private synchronized void dumpCounters() { // Headers System.out.print(String.format("%30s ", "")); getRequestKindNames().forEach(kindName -> System.out.print(" " + kindName.substring(0, 2))); System.out.println(""); // Table rows hostToCounterMap.forEach((host, counters) -> { System.out.println(String.format("%30s: %s", "[" + host + "]", counters)); }); } private static Stream<String> getRequestKindNames() { return Arrays.stream(RequestKind.values()).map(kind -> kind.shortName); } public synchronized void reset(final URL frameUrl) { hostToCounterMap = new HashMap<>(); final String frameHostOrig = frameUrl.getHost(); final String frameHost = frameHostOrig == null ? "" : frameHostOrig.toLowerCase(); ensureHostInCounter(frameHost); setupPermissionSystem(frameHost); } public void manageRequests(final JComponent initiatorComponent) { // permissionSystemOpt.ifPresent(r -> r.dump()); final ManageDialog dlg = new ManageDialog(new JFrame(), getFrameURL().map(u -> u.toExternalForm()).orElse("Empty!"), initiatorComponent); dlg.setVisible(true); } private synchronized String[][] getRequestData() { // hostToCounterMap.keySet().stream().forEach(System.out::println); return hostToCounterMap.entrySet().stream().map(entry -> { final List<String> rowElements = new LinkedList<>(); rowElements.add(entry.getKey()); Arrays.stream(entry.getValue().counters).forEach(c -> rowElements.add(Integer.toString(c))); return rowElements.toArray(new String[0]); }).toArray(String[][]::new); } private static String[] getColumnNames() { final List<String> kindNames = getRequestKindNames().collect(Collectors.toList()); kindNames.add(0, "All"); return kindNames.toArray(new String[0]); } public final class ManageDialog extends JDialog implements ActionListener { private static final long serialVersionUID = -2284357432219717106L; private final JComponent initiator; public ManageDialog(final JFrame parent, final String title, final JComponent initiator) { super(parent, title, true); this.initiator = initiator; setUndecorated(true); if (parent != null) { final Dimension parentSize = parent.getSize(); final Point p = parent.getLocation(); setLocation(p.x + (parentSize.width / 4), p.y + (parentSize.height / 4)); } final JComponent table = PermissionTable.makeTable(permissionSystemOpt.get(), getColumnNames(), getRequestData()); final JScrollPane scrollTablePane = new JScrollPane(table); getContentPane().add(scrollTablePane); final JPanel buttonPane = new JPanel(); final JButton button = new JButton("OK"); buttonPane.add(button); button.addActionListener(this); getContentPane().add(buttonPane, BorderLayout.SOUTH); setDefaultCloseOperation(DISPOSE_ON_CLOSE); addWindowListener(new WindowListenerImpl()); pack(); updateLocation(); initiator.addHierarchyBoundsListener(new HierarchyBoundsListener() { @Override public void ancestorResized(final HierarchyEvent e) { updateLocation(); } @Override public void ancestorMoved(final HierarchyEvent e) { updateLocation(); } }); GUITasks.addEscapeListener(this); } public void actionPerformed(final ActionEvent e) { setVisible(false); dispose(); } private void updateLocation() { final Point locationOnScreen = initiator.getLocationOnScreen(); locationOnScreen.translate(initiator.getWidth() - getWidth(), initiator.getHeight()); setLocation(locationOnScreen); } private final class WindowListenerImpl implements WindowListener { private String initialPermissionStates; @Override public void windowOpened(final WindowEvent e) { initialPermissionStates = permissionSystemOpt.get().getPermissionsAsString(); } @Override public void windowIconified(final WindowEvent e) { } @Override public void windowDeiconified(final WindowEvent e) { } @Override public void windowDeactivated(final WindowEvent e) { } @Override public void windowClosing(final WindowEvent e) { } @Override public void windowClosed(final WindowEvent e) { final String finalPermissionStates = permissionSystemOpt.get().getPermissionsAsString(); if (!finalPermissionStates.equals(initialPermissionStates)) { frame.reload(); } } @Override public void windowActivated(final WindowEvent e) { } } } public void allowAllFirstPartyRequests() { permissionSystemOpt.ifPresent(p -> { final PermissionRow row = p.getLastBoard().getRow(getFrameHost().get()); row.getHostCell().setPermission(Permission.Allow); }); } }