/* GNU GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project 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 verion 2 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Contact info: lobochief@users.sourceforge.net */ /* * Created on Mar 14, 2005 */ package org.lobobrowser.protocol.about; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collection; import java.util.Properties; import java.util.TreeSet; import org.lobobrowser.main.PlatformInit; import org.lobobrowser.primary.ext.BookmarkInfo; import org.lobobrowser.primary.ext.BookmarksHistory; import org.lobobrowser.primary.ext.HistoryEntry; import org.lobobrowser.primary.settings.SearchEngine; import org.lobobrowser.primary.settings.ToolsSettings; import org.lobobrowser.util.Strings; import org.lobobrowser.util.Timing; /** * @author J. H. S. */ public class AboutURLConnection extends URLConnection { public AboutURLConnection(final URL url) { super(url); } /* * (non-Javadoc) * * @see java.net.URLConnection#connect() */ @Override public void connect() throws IOException { } /* * (non-Javadoc) * * @see java.net.URLConnection#getContentLength() */ @Override public int getContentLength() { return -1; } /* * (non-Javadoc) * * @see java.net.URLConnection#getContentType() */ @Override public String getContentType() { return "text/html"; } /* * (non-Javadoc) * * @see java.net.URLConnection#getInputStream() */ @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(this.getURLText(this.getURL()).getBytes("UTF-8")); } private String getURLText(final java.net.URL url) { final String path = url.getPath(); if ("blank".equalsIgnoreCase(path)) { return ""; } else if ("bookmarks".equalsIgnoreCase(path)) { return getBookmarks(); } else if ("bookmark-search".equalsIgnoreCase(path)) { String query = url.getQuery(); if (query == null) { query = ""; } try { final String searchQuery = java.net.URLDecoder.decode(query, "UTF-8"); return this.getBookmarks(searchQuery); } catch (final java.io.UnsupportedEncodingException uee) { throw new IllegalStateException("not expected", uee); } } else if ("java-properties".equals(path)) { return getSystemProperties(); } else if ("welcome".equals(path)) { return getWelcomeMessage(); } else if ("confirmSearch".equals(path)) { String query = url.getQuery(); if (query == null) { query = ""; } try { final String searchQuery = java.net.URLDecoder.decode(query, "UTF-8"); return AboutURLConnection.getSearchConfirmation(searchQuery); } catch (final java.io.UnsupportedEncodingException uee) { throw new IllegalStateException("not expected", uee); } } else { return "<p>Unknown about path: " + path + "</p>" + "<h3>Known paths are:</h3>" + "<ul>" + "<li><a href='about:blank'>about:blank</a></li>" + "<li><a href='about:bookmarks'>about:bookmarks</a></li>" + "<li><a href='about:bookmark-search?term'>about:bookmark-search?term</a></li>" + "<li><a href='about:java-properties'>about:java-properties</a></li>" + "</ul>"; } } private static String getSearchConfirmation(final String searchQuery) { final ToolsSettings settings = ToolsSettings.getInstance(); final Collection<SearchEngine> searchEngines = settings.getSearchEngines(); final StringWriter swriter = new StringWriter(); final PrintWriter writer = new PrintWriter(swriter); writer.println("<html>"); writer.println("<head><title>Confirm Search<title></head>"); writer.println("<body>"); writer.println("<h3>The address bar text was ambiguous.</h3>"); final String directURL = "https://" + searchQuery; writer.println("<p>Click <a href='" + directURL + "'>here</a> to continue to <span style='font-family:mono'>"+directURL+"</span></p>"); if (searchEngines.size() == 0) { writer.println("No search engines were found."); } else { writer.println("<h3>Confirm external search by selecting a search engine:</h3>"); writer.println("<ol>"); for (final SearchEngine searchEngine : searchEngines) { writeSearchEngineEntry(writer, searchEngine, searchQuery); } writer.println("</ol>"); } writer.println("</body>"); writer.println("</html>"); writer.flush(); return swriter.toString(); } private static void writeSearchEngineEntry(final PrintWriter writer, final SearchEngine searchEngine, final String searchQuery) { final ToolsSettings settings = ToolsSettings.getInstance(); java.net.URL url = null; try { url = searchEngine.getURL(searchQuery); } catch (final java.net.MalformedURLException mfu) { mfu.printStackTrace(); } final String urlText = "Search with " + searchEngine.getName(); writer.println("<LI>"); writer.println("<DIV>"); writer.println("<A href='" + url + "'>" + urlText + "</A>"); if (searchEngine.equals(settings.getSelectedSearchEngine())) { writer.print("(Default)"); } writer.println("<BR><BR>"); writer.println("</DIV>"); writer.println("</LI>"); } private static String getWelcomeMessage() { final Properties relProps = PlatformInit.getInstance().relProps; return "<div style='max-width:900px;margin:0 auto;text-align:center;'>" + "<h1>Welcome to gngr</h1>" + "<p>Version: " + relProps.getProperty(PlatformInit.RELEASE_VERSION_STRING) + "<br/>" + "Published on: " + relProps.getProperty(PlatformInit.RELEASE_VERSION_RELEASE_DATE) + "</p>" + "<p><b><a href='https://gngr.info'>gngr</a></b> is a browser that cares deeply about privacy.</p>" + "<p>It is currently a proof-of-concept, and not very stable or secure.</p>" + "<div style='text-align:left;padding:1em;margin:1em auto; width:50em;background:#ffd;border:1px solid #bbb'>" + "<p>We recommend that you use this version for casual browsing only and follow the project's <a href='https://blog.gngr.info'>blog</a> to stay abreast of changes.</p>" + "<p>Other ways of reaching us:</p>" + "<ul>" + "<li style='margin:1em 0'><a href='https://github.com/uprootlabs/gngr'>Source code and issues</a> on GitHub</li>" + "<li style='margin:1em 0'>#gngr and #gngr-dev on Freenode IRC</li>" + "<li style='margin:1em 0'><a href='https://reddit.com/r/gngr'>/r/gngr</a> on Reddit</li>" + "<li style='margin:1em 0'><a href='https://twitter.com/gngrInfo'>@gngrInfo</a> on Twitter</li>" + "</ul>" + "</div>" + "<div style='padding:1em;border:1px solid #bbb;background:#efe;width:50em;margin:0 auto'>" + "<p><span style='border-bottom:2px solid red; font-weight:bold'>Tip:</span> Checkout the Request Manager button on the right of the URL bar. " + "The Request Manager allows you to control which URL requests are allowed on a given webpage.</p>" + "<p>By default, cookies, scripts and frames are disabled on all websites. " + "You can change these rules as per your preferences.</p>" + "<p>Note: the button is disabled on this page since it is an internal page and there are no external requests.</p>" + "<div style='position:fixed; right: 0; top: 0; background: #efe; color:#595; padding: 0.33em 1em; border:2px dotted #9f9; border-top:0'>" + "<p style='margin:0; font-weight:bold; text-align:right; font-size:120% '>⬆</p>" + "<p style='margin:0; font-weight:bold'>Request Manager</p>" + "<p style='margin:0; font-size: 90%'>(read the tip below)</p>" + "</div>" + "</div>" + "</div>"; } private static String getSystemProperties() { final StringWriter swriter = new StringWriter(); final PrintWriter writer = new PrintWriter(swriter); writer.println("<html>"); writer.println("<head><title>Java Properties</title></head>"); writer.println("<body>"); writer.println("<pre>"); final Properties properties = System.getProperties(); properties.list(writer); writer.println("</pre>"); writer.println("</body>"); writer.println("</html>"); writer.flush(); return swriter.toString(); } private static String getBookmarks() { final BookmarksHistory history = BookmarksHistory.getInstance(); return getBookmarks(history.getAllEntries()); } private String getBookmarks(final String searchQuery) { // This is more of a scan. Not efficient but it does the // job for now considering the number of entries is limited. final String[] keywords = Strings.split(searchQuery); final BookmarksHistory history = BookmarksHistory.getInstance(); final Collection<HistoryEntry<BookmarkInfo>> entries = history.getAllEntries(); final Collection<ScoredEntry> sortedEntries = new TreeSet<>(); for (final HistoryEntry<BookmarkInfo> entry : entries) { final int matchScore = getMatchScore(entry.getItemInfo(), keywords); if (matchScore > 0) { sortedEntries.add(new ScoredEntry(entry, matchScore)); } } final Collection<HistoryEntry<BookmarkInfo>> finalEntries = new ArrayList<>(); for (final ScoredEntry scoredEntry : sortedEntries) { finalEntries.add(scoredEntry.getHistoryEntry()); } return getBookmarks(finalEntries); } private static int getMatchScore(final BookmarkInfo binfo, final String[] keywords) { int total = 0; for (final String keyword : keywords) { final int score = getMatchScore(binfo, keyword); if (score == 0) { return 0; } total += score; } return total; } private static int getMatchScore(final BookmarkInfo binfo, final String keyword) { final String keywordTL = keyword.toLowerCase(); int score = 0; final String urlText = binfo.getUrl().toExternalForm(); if (urlText.contains(keyword)) { score += 3; } else if (urlText.toLowerCase().contains(keywordTL)) { score += 2; } final String title = binfo.getTitle(); if ((title != null) && title.contains(keyword)) { score += 8; } else if ((title != null) && title.toLowerCase().contains(keywordTL)) { score += 6; } final String description = binfo.getDescription(); if ((description != null) && description.contains(keyword)) { score += 3; } else if ((description != null) && description.toLowerCase().contains(keywordTL)) { score += 2; } final String[] tags = binfo.getTags(); if (tags != null) { for (final String tag : tags) { if (tag.equals(keyword)) { score += 8; } else if (tag.toLowerCase().equals(keywordTL)) { score += 6; } } } return score; } private static String getBookmarks(final Collection<HistoryEntry<BookmarkInfo>> entries) { final StringWriter swriter = new StringWriter(); final PrintWriter writer = new PrintWriter(swriter); writer.println("<html>"); writer.println("<head>Bookmarks</head>"); writer.println("<body>"); if (entries.size() == 0) { writer.println("No bookmarks were found."); } else { writer.println("<h3>Bookmarks</h3>"); writer.println("<ol>"); for (final HistoryEntry<BookmarkInfo> entry : entries) { writeBookmark(writer, entry); } writer.println("</ol>"); } writer.println("</body>"); writer.println("</html>"); writer.flush(); return swriter.toString(); } private static void writeBookmark(final PrintWriter writer, final HistoryEntry<BookmarkInfo> entry) { final java.net.URL url = entry.getUrl(); final String urlText = url.toExternalForm(); final BookmarkInfo binfo = entry.getItemInfo(); String text = binfo.getTitle(); if ((text == null) || (text.length() == 0)) { text = urlText; } final long elapsed = System.currentTimeMillis() - entry.getTimetstamp(); String description = binfo.getDescription(); if (description == null) { description = ""; } writer.println("<LI>"); writer.println("<DIV>"); writer.println("<A href='" + urlText + "'>" + text + "</A> (" + Timing.getElapsedText(elapsed) + " ago)"); writer.println("</DIV>"); writer.println("<DIV>"); writer.println(description); writer.println("</DIV>"); writer.println("</LI>"); } private class ScoredEntry implements Comparable<ScoredEntry> { private final HistoryEntry<BookmarkInfo> historyEntry; private final int score; public ScoredEntry(final HistoryEntry<BookmarkInfo> historyEntry, final int score) { super(); this.historyEntry = historyEntry; this.score = score; } public HistoryEntry<BookmarkInfo> getHistoryEntry() { return historyEntry; } public int getScore() { return score; } public int compareTo(final ScoredEntry o) { if (this == o) { return 0; } final ScoredEntry other = o; int diff = other.score - this.score; if (diff != 0) { return diff; } diff = (int) (other.historyEntry.getTimetstamp() - this.historyEntry.getTimetstamp()); if (diff != 0) { return diff; } diff = System.identityHashCode(other) - System.identityHashCode(this); if (diff != 0) { return diff; } else { return System.identityHashCode(other.historyEntry) - System.identityHashCode(this.historyEntry); } } @Override public int hashCode() { return this.score; } } }