X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Flibrary%2FWebLibraryServer.java;h=22f36e91f5729a7bfef72fe9c98d2a7fa157a6ec;hp=97f2f0e8f69c6c07e217e1022ee4aa0fa96eaa4d;hb=cab46763df87b1aacf51a9c064a3f5fc49642df9;hpb=33d40b9f752a30ba2c9ed09a554a63cf6083de32 diff --git a/src/be/nikiroo/fanfix/library/WebLibraryServer.java b/src/be/nikiroo/fanfix/library/WebLibraryServer.java index 97f2f0e..22f36e9 100644 --- a/src/be/nikiroo/fanfix/library/WebLibraryServer.java +++ b/src/be/nikiroo/fanfix/library/WebLibraryServer.java @@ -3,7 +3,9 @@ package be.nikiroo.fanfix.library; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -25,13 +27,10 @@ import be.nikiroo.utils.LoginResult; import be.nikiroo.utils.NanoHTTPD; import be.nikiroo.utils.NanoHTTPD.Response; import be.nikiroo.utils.NanoHTTPD.Response.Status; +import be.nikiroo.utils.Progress; public class WebLibraryServer extends WebLibraryServerHtml { class WLoginResult extends LoginResult { - private boolean rw; - private boolean wl; - private boolean bl; - public WLoginResult(boolean badLogin, boolean badCookie) { super(badLogin, badCookie); } @@ -40,9 +39,6 @@ public class WebLibraryServer extends WebLibraryServerHtml { boolean wl, boolean bl) { super(who, key, subkey, (rw ? "|rw" : "") + (wl ? "|wl" : "") + (bl ? "|bl" : "") + "|"); - this.rw = rw; - this.wl = wl; - this.bl = bl; } public WLoginResult(String cookie, String who, String key, @@ -72,6 +68,10 @@ public class WebLibraryServer extends WebLibraryServerHtml { private List whitelist; private List blacklist; + private Map imprts = new HashMap(); + + private boolean exiting; + public WebLibraryServer(boolean secure) throws IOException { super(secure); @@ -99,6 +99,56 @@ public class WebLibraryServer extends WebLibraryServerHtml { new Thread(this).start(); } + @Override + protected Response stop(WLoginResult login) { + if (!login.isRw()) { + return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN, + NanoHTTPD.MIME_PLAINTEXT, "Exit not allowed"); + } + + if (exiting) { + return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE, + NanoHTTPD.MIME_PLAINTEXT, "Server is already exiting..."); + } + + exiting = true; + Instance.getInstance().getTraceHandler().trace("Exiting"); + + boolean ok; + do { + synchronized (imprts) { + ok = imprts.isEmpty(); + } + if (!ok) { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + Instance.getInstance().getTraceHandler() + .trace("Waiting to exit..."); + } + } + } while (!ok); + + doStop(); + + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + } + + Instance.getInstance().getTraceHandler() + .trace("Exit timeout: force-quit"); + System.exit(0); + } + }, "Exit program after timeout of 1500 ms").start(); + + return NanoHTTPD.newFixedLengthResponse(Status.OK, + NanoHTTPD.MIME_PLAINTEXT, "Exited"); + } + @Override protected WLoginResult login(boolean badLogin, boolean badCookie) { return new WLoginResult(false, false); @@ -182,17 +232,17 @@ public class WebLibraryServer extends WebLibraryServerHtml { // /story/luid/json <-- json, whole chapter (no images) @Override protected Response getStoryPart(String uri, WLoginResult login) { - String[] cover = uri.split("/"); + String[] uriParts = uri.split("/"); int off = 2; - if (cover.length < off + 2) { + if (uriParts.length < off + 2) { return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, null); } - String luid = cover[off + 0]; - String chapterStr = cover[off + 1]; - String imageStr = cover.length < off + 3 ? null : cover[off + 2]; + String luid = uriParts[off + 0]; + String chapterStr = uriParts[off + 1]; + String imageStr = uriParts.length < off + 3 ? null : uriParts[off + 2]; // 1-based (0 = desc) int chapter = 0; @@ -228,7 +278,7 @@ public class WebLibraryServer extends WebLibraryServerHtml { InputStream in = null; try { if ("cover".equals(chapterStr)) { - Image img = cover(luid, login); + Image img = storyCover(luid, login); if (img != null) { in = img.newInputStream(); } @@ -287,6 +337,226 @@ public class WebLibraryServer extends WebLibraryServerHtml { return newInputStreamResponse(mimeType, in); } + // /story/luid/source + // /story/luid/title + // /story/luid/author + @Override + protected Response setStoryPart(String uri, String value, + WLoginResult login) throws IOException { + String[] uriParts = uri.split("/"); + int off = 2; // "" and "story" + + if (uriParts.length < off + 2) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, "Invalid story part request"); + } + + if (!login.isRw()) { + return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN, + NanoHTTPD.MIME_PLAINTEXT, "SET story part not allowed"); + } + + if (exiting) { + return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE, + NanoHTTPD.MIME_PLAINTEXT, "Server is exiting..."); + } + + String luid = uriParts[off + 0]; + String type = uriParts[off + 1]; + + if (!Arrays.asList("source", "title", "author").contains(type)) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, + "Invalid SET story part: " + type); + } + + if (meta(luid, login) != null) { + BasicLibrary lib = Instance.getInstance().getLibrary(); + if ("source".equals(type)) { + lib.changeSource(luid, value, null); + } else if ("title".equals(type)) { + lib.changeTitle(luid, value, null); + } else if ("author".equals(type)) { + lib.changeAuthor(luid, value, null); + } + } + + return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null); + } + + @Override + protected Response getCover(String uri, WLoginResult login) + throws IOException { + String[] uriParts = uri.split("/"); + int off = 2; // "" and "cover" + + if (uriParts.length < off + 2) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request"); + } + + String type = uriParts[off + 0]; + String id = uriParts[off + 1]; + + InputStream in = null; + + if ("story".equals(type)) { + Image img = storyCover(id, login); + if (img != null) { + in = img.newInputStream(); + } + } else if ("source".equals(type)) { + Image img = sourceCover(id, login); + if (img != null) { + in = img.newInputStream(); + } + } else if ("author".equals(type)) { + Image img = authorCover(id, login); + if (img != null) { + in = img.newInputStream(); + } + } else { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, + "Invalid GET cover type: " + type); + } + + // TODO: get correct image type + return newInputStreamResponse("image/png", in); + } + + @Override + protected Response setCover(String uri, String luid, WLoginResult login) + throws IOException { + String[] uriParts = uri.split("/"); + int off = 2; // "" and "cover" + + if (uriParts.length < off + 2) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request"); + } + + if (!login.isRw()) { + return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN, + NanoHTTPD.MIME_PLAINTEXT, "Cover request not allowed"); + } + + if (exiting) { + return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE, + NanoHTTPD.MIME_PLAINTEXT, "Server is exiting..."); + } + + String type = uriParts[off + 0]; + String id = uriParts[off + 1]; + + if ("source".equals(type)) { + sourceCover(id, login, luid); + } else if ("author".equals(type)) { + authorCover(id, login, luid); + } else { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, + "Invalid SET cover type: " + type); + } + + return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null); + } + + @Override + protected Response imprt(String uri, String urlStr, WLoginResult login) + throws IOException { + final BasicLibrary lib = Instance.getInstance().getLibrary(); + + if (!login.isRw()) { + return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN, + NanoHTTPD.MIME_PLAINTEXT, "Import not allowed"); + } + + if (exiting) { + return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE, + NanoHTTPD.MIME_PLAINTEXT, "Server is exiting..."); + } + + final URL url = new URL(urlStr); + final Progress pg = new Progress(); + final String luid = lib.getNextId(); + + synchronized (imprts) { + imprts.put(luid, pg); + } + + new Thread(new Runnable() { + @Override + public void run() { + try { + lib.imprt(url, luid, pg); + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); + } finally { + synchronized (imprts) { + imprts.remove(luid); + } + } + } + }, "Import story: " + urlStr).start(); + + return NanoHTTPD.newFixedLengthResponse(Status.OK, + NanoHTTPD.MIME_PLAINTEXT, luid); + } + + @Override + protected Response imprtProgress(String uri, WLoginResult login) { + String[] uriParts = uri.split("/"); + int off = 2; // "" and "import" + + if (uriParts.length < off + 1) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request"); + } + + String luid = uriParts[off + 0]; + + Progress pg = null; + synchronized (imprts) { + pg = imprts.get(luid); + } + if (pg != null) { + return NanoHTTPD.newFixedLengthResponse(Status.OK, + "application/json", JsonIO.toJson(pg).toString()); + } + + return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null); + } + + @Override + protected Response delete(String uri, WLoginResult login) + throws IOException { + String[] uriParts = uri.split("/"); + int off = 2; // "" and "delete" + + if (uriParts.length < off + 1) { + return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST, + NanoHTTPD.MIME_PLAINTEXT, "Invalid delete request"); + } + + if (!login.isRw()) { + return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN, + NanoHTTPD.MIME_PLAINTEXT, "Delete not allowed"); + } + + if (exiting) { + return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE, + NanoHTTPD.MIME_PLAINTEXT, "Server is exiting..."); + } + + String luid = uriParts[off + 0]; + + BasicLibrary lib = Instance.getInstance().getLibrary(); + lib.delete(luid); + + return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null); + } + @Override protected List metas(WLoginResult login) throws IOException { BasicLibrary lib = Instance.getInstance().getLibrary(); @@ -348,7 +618,8 @@ public class WebLibraryServer extends WebLibraryServerHtml { return meta; } - private Image cover(String luid, WLoginResult login) throws IOException { + private Image storyCover(String luid, WLoginResult login) + throws IOException { MetaData meta = meta(luid, login); if (meta != null) { BasicLibrary lib = Instance.getInstance().getLibrary(); @@ -358,13 +629,74 @@ public class WebLibraryServer extends WebLibraryServerHtml { return null; } + private Image authorCover(String author, WLoginResult login) + throws IOException { + Image img = null; + + List metas = new MetaResultList(metas(login)).filter(null, + author, null); + if (metas.size() > 0) { + BasicLibrary lib = Instance.getInstance().getLibrary(); + img = lib.getCustomAuthorCover(author); + if (img == null) + img = lib.getCover(metas.get(0).getLuid()); + } + + return img; + + } + + private void authorCover(String author, WLoginResult login, String luid) + throws IOException { + if (meta(luid, login) != null) { + List metas = new MetaResultList(metas(login)).filter(null, + author, null); + if (metas.size() > 0) { + BasicLibrary lib = Instance.getInstance().getLibrary(); + lib.setAuthorCover(author, luid); + } + } + } + + private Image sourceCover(String source, WLoginResult login) + throws IOException { + Image img = null; + + List metas = new MetaResultList(metas(login)).filter(source, + null, null); + if (metas.size() > 0) { + BasicLibrary lib = Instance.getInstance().getLibrary(); + img = lib.getCustomSourceCover(source); + if (img == null) + img = lib.getCover(metas.get(0).getLuid()); + } + + return img; + } + + private void sourceCover(String source, WLoginResult login, String luid) + throws IOException { + if (meta(luid, login) != null) { + List metas = new MetaResultList(metas(login)) + .filter(source, null, null); + if (metas.size() > 0) { + BasicLibrary lib = Instance.getInstance().getLibrary(); + lib.setSourceCover(source, luid); + } + } + } + private boolean isAllowed(MetaData meta, WLoginResult login) { - if (login.isWl() && !whitelist.isEmpty() - && !whitelist.contains(meta.getSource())) { - return false; + MetaResultList one = new MetaResultList(Arrays.asList(meta)); + if (login.isWl() && !whitelist.isEmpty()) { + if (one.filter(whitelist, null, null).isEmpty()) { + return false; + } } - if (login.isBl() && blacklist.contains(meta.getSource())) { - return false; + if (login.isBl() && !blacklist.isEmpty()) { + if (!one.filter(blacklist, null, null).isEmpty()) { + return false; + } } return true;