package be.nikiroo.fanfix.library; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLServerSocketFactory; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Paragraph.ParagraphType; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.library.WebLibraryServer.WLoginResult; import be.nikiroo.fanfix.library.web.WebLibraryServerIndex; import be.nikiroo.fanfix.library.web.templates.WebLibraryServerTemplates; import be.nikiroo.fanfix.reader.TextOutput; import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.NanoHTTPD; import be.nikiroo.utils.NanoHTTPD.IHTTPSession; import be.nikiroo.utils.NanoHTTPD.Response; import be.nikiroo.utils.NanoHTTPD.Response.Status; import be.nikiroo.utils.TraceHandler; import be.nikiroo.utils.Version; abstract class WebLibraryServerHtml implements Runnable { private NanoHTTPD server; protected TraceHandler tracer = new TraceHandler(); WebLibraryServerTemplates templates = WebLibraryServerTemplates .getInstance(); abstract protected WLoginResult login(String who, String cookie); abstract protected WLoginResult login(String who, String key, String subkey); abstract protected WLoginResult login(boolean badLogin, boolean badCookie); abstract protected Response getList(String uri, WLoginResult login) throws IOException; abstract protected Response getStoryPart(String uri, WLoginResult login); abstract protected Response setStoryPart(String uri, String value, WLoginResult login) throws IOException; abstract protected Response getCover(String uri, WLoginResult login) throws IOException; abstract protected Response setCover(String uri, String luid, WLoginResult login) throws IOException; abstract protected List metas(WLoginResult login) throws IOException; abstract protected Story story(String luid, WLoginResult login) throws IOException; protected abstract Response imprt(String uri, String url, WLoginResult login) throws IOException; protected abstract Response imprtProgress(String uri, WLoginResult login); protected abstract Response delete(String uri, WLoginResult login) throws IOException; /** * Wait until all operations are done and stop the server. *

* All the new R/W operations will be refused after a call to stop. */ protected abstract Response stop(WLoginResult login); public WebLibraryServerHtml(boolean secure) throws IOException { Integer port = Instance.getInstance().getConfig() .getInteger(Config.SERVER_PORT); if (port == null) { throw new IOException( "Cannot start web server: port not specified"); } SSLServerSocketFactory ssf = null; if (secure) { String keystorePath = Instance.getInstance().getConfig() .getString(Config.SERVER_SSL_KEYSTORE, ""); String keystorePass = Instance.getInstance().getConfig() .getString(Config.SERVER_SSL_KEYSTORE_PASS); if (secure && keystorePath.isEmpty()) { throw new IOException( "Cannot start a secure web server: no keystore.jks file povided"); } if (!keystorePath.isEmpty()) { File keystoreFile = new File(keystorePath); try { KeyStore keystore = KeyStore .getInstance(KeyStore.getDefaultType()); InputStream keystoreStream = new FileInputStream( keystoreFile); try { keystore.load(keystoreStream, keystorePass.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory .getDefaultAlgorithm()); keyManagerFactory.init(keystore, keystorePass.toCharArray()); ssf = NanoHTTPD.makeSSLSocketFactory(keystore, keyManagerFactory); } finally { keystoreStream.close(); } } catch (Exception e) { throw new IOException(e.getMessage()); } } } server = new NanoHTTPD(port) { @Override public Response serve(final IHTTPSession session) { super.serve(session); String query = session.getQueryParameterString(); // a=a%20b&dd=2 Method method = session.getMethod(); // GET, POST.. String uri = session.getUri(); // /home.html // need them in real time (not just those sent by the UA) Map cookies = new HashMap(); for (String cookie : session.getCookies()) { cookies.put(cookie, session.getCookies().read(cookie)); } WLoginResult login = null; Map params = session.getParms(); String who = session.getRemoteHostName() + session.getRemoteIpAddress(); if (params.get("login") != null) { login = login(who, params.get("password"), params.get("login")); } else { String cookie = cookies.get("cookie"); login = login(who, cookie); } if (login.isSuccess()) { // refresh cookie session.getCookies().set(new Cookie("cookie", login.getCookie(), "30; path=/")); // set options String optionName = params.get("optionName"); if (optionName != null && !optionName.isEmpty()) { String optionNo = params.get("optionNo"); String optionValue = params.get("optionValue"); if (optionNo != null || optionValue == null || optionValue.isEmpty()) { session.getCookies().delete(optionName); cookies.remove(optionName); } else { session.getCookies().set(new Cookie(optionName, optionValue, "; path=/")); cookies.put(optionName, optionValue); } } } Response rep = null; try { if (!login.isSuccess() && WebLibraryUrls.isSupportedUrl(uri, true)) { rep = loginPage(login, uri); } if (rep == null) { if (WebLibraryUrls.isSupportedUrl(uri, false)) { if (WebLibraryUrls.INDEX_URL.equals(uri)) { rep = root(session, cookies, login); } else if (WebLibraryUrls.VERSION_URL.equals(uri)) { rep = newFixedLengthResponse(Status.OK, MIME_PLAINTEXT, Version.getCurrentVersion().toString()); } else if (WebLibraryUrls.isCoverUrl(uri)) { String luid = params.get("luid"); if (luid != null) { rep = setCover(uri, luid, login); } else { rep = getCover(uri, login); } } else if (WebLibraryUrls.isListUrl(uri)) { rep = getList(uri, login); } else if (WebLibraryUrls.isStoryUrl(uri)) { String value = params.get("value"); if (value != null) { rep = setStoryPart(uri, value, login); } else { rep = getStoryPart(uri, login); } } else if (WebLibraryUrls.isViewUrl(uri)) { rep = getViewer(cookies, uri, login); } else if (WebLibraryUrls.LOGOUT_URL.equals(uri)) { session.getCookies().delete("cookie"); cookies.remove("cookie"); rep = loginPage(login(false, false), uri); } else if (WebLibraryUrls.isImprtUrl(uri)) { String url = params.get("url"); if (url != null) { rep = imprt(uri, url, login); } else { rep = imprtProgress(uri, login); } } else if (WebLibraryUrls.isDeleteUrl(uri)) { rep = delete(uri, login); } else if (WebLibraryUrls.EXIT_URL.equals(uri)) { rep = WebLibraryServerHtml.this.stop(login); } else { getTraceHandler().error( "Supported URL was not processed: " + uri); rep = newFixedLengthResponse( Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "An error happened"); } } else { if (uri.startsWith("/")) uri = uri.substring(1); InputStream in = IOUtils.openResource( WebLibraryServerIndex.class, uri); if (in != null) { String mimeType = MIME_PLAINTEXT; if (uri.endsWith(".css")) { mimeType = "text/css"; } else if (uri.endsWith(".html")) { mimeType = "text/html"; } else if (uri.endsWith(".js")) { mimeType = "text/javascript"; } else if (uri.endsWith(".png")) { mimeType = "image/png"; } else if (uri.endsWith(".ico")) { mimeType = "image/x-icon"; } else if (uri.endsWith(".java")) { mimeType = "text/plain"; } rep = newChunkedResponse(Status.OK, mimeType, in); } if (rep == null) { getTraceHandler().trace("404: " + uri); rep = newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); } } } } catch (Exception e) { Instance.getInstance().getTraceHandler().error( new IOException("Cannot process web request", e)); rep = newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "An error occured"); } return rep; } }; if (ssf != null) { getTraceHandler().trace("Install SSL on the web server..."); server.makeSecure(ssf, null); getTraceHandler().trace("Done."); } } @Override public void run() { try { server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } catch (IOException e) { tracer.error(new IOException("Cannot start the web server", e)); } } protected void doStop() { server.stop(); } /** * The traces handler for this {@link WebLibraryServerHtml}. * * @return the traces handler */ public TraceHandler getTraceHandler() { return tracer; } /** * The traces handler for this {@link WebLibraryServerHtml}. * * @param tracer * the new traces handler */ public void setTraceHandler(TraceHandler tracer) { if (tracer == null) { tracer = new TraceHandler(false, false, false); } this.tracer = tracer; } private Response loginPage(WLoginResult login, String uri) throws IOException { List