fix STOP server
[fanfix.git] / src / be / nikiroo / fanfix / library / WebLibraryServer.java
CommitLineData
f433d153
NR
1package be.nikiroo.fanfix.library;
2
3import java.io.ByteArrayInputStream;
f433d153
NR
4import java.io.IOException;
5import java.io.InputStream;
f70bcacf 6import java.net.URL;
f433d153 7import java.util.ArrayList;
5c4ce687 8import java.util.Arrays;
f433d153
NR
9import java.util.HashMap;
10import java.util.LinkedList;
11import java.util.List;
12import java.util.Map;
13
f433d153
NR
14import org.json.JSONArray;
15import org.json.JSONObject;
16
17import be.nikiroo.fanfix.Instance;
18import be.nikiroo.fanfix.bundles.Config;
f433d153
NR
19import be.nikiroo.fanfix.data.Chapter;
20import be.nikiroo.fanfix.data.JsonIO;
21import be.nikiroo.fanfix.data.MetaData;
22import be.nikiroo.fanfix.data.Paragraph;
23import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
24import be.nikiroo.fanfix.data.Story;
f433d153 25import be.nikiroo.utils.Image;
fce0a73f 26import be.nikiroo.utils.LoginResult;
f433d153 27import be.nikiroo.utils.NanoHTTPD;
f433d153
NR
28import be.nikiroo.utils.NanoHTTPD.Response;
29import be.nikiroo.utils.NanoHTTPD.Response.Status;
f70bcacf 30import be.nikiroo.utils.Progress;
f433d153 31
33d40b9f
NR
32public class WebLibraryServer extends WebLibraryServerHtml {
33 class WLoginResult extends LoginResult {
fce0a73f
NR
34 public WLoginResult(boolean badLogin, boolean badCookie) {
35 super(badLogin, badCookie);
36 }
37
38 public WLoginResult(String who, String key, String subkey, boolean rw,
39 boolean wl, boolean bl) {
40 super(who, key, subkey, (rw ? "|rw" : "") + (wl ? "|wl" : "")
41 + (bl ? "|bl" : "") + "|");
f433d153
NR
42 }
43
fce0a73f 44 public WLoginResult(String cookie, String who, String key,
f433d153 45 List<String> subkeys) {
fce0a73f
NR
46 super(cookie, who, key, subkeys,
47 subkeys == null || subkeys.isEmpty());
f433d153
NR
48 }
49
50 public boolean isRw() {
fce0a73f 51 return getOption().contains("|rw|");
f433d153
NR
52 }
53
54 public boolean isWl() {
fce0a73f 55 return getOption().contains("|wl|");
f433d153
NR
56 }
57
d11fb35b 58 public boolean isBl() {
fce0a73f 59 return getOption().contains("|bl|");
f433d153
NR
60 }
61 }
62
f433d153
NR
63 private Map<String, Story> storyCache = new HashMap<String, Story>();
64 private LinkedList<String> storyCacheOrder = new LinkedList<String>();
65 private long storyCacheSize = 0;
66 private long maxStoryCacheSize;
f433d153 67
d11fb35b
NR
68 private List<String> whitelist;
69 private List<String> blacklist;
70
f70bcacf
NR
71 private Map<String, Progress> imprts = new HashMap<String, Progress>();
72
09c2396e
NR
73 private boolean exiting;
74
f433d153 75 public WebLibraryServer(boolean secure) throws IOException {
33d40b9f 76 super(secure);
f433d153
NR
77
78 int cacheMb = Instance.getInstance().getConfig()
79 .getInteger(Config.SERVER_MAX_CACHE_MB, 100);
80 maxStoryCacheSize = cacheMb * 1024 * 1024;
81
82 setTraceHandler(Instance.getInstance().getTraceHandler());
83
d11fb35b
NR
84 whitelist = Instance.getInstance().getConfig()
85 .getList(Config.SERVER_WHITELIST, new ArrayList<String>());
86 blacklist = Instance.getInstance().getConfig()
87 .getList(Config.SERVER_BLACKLIST, new ArrayList<String>());
f433d153
NR
88 }
89
90 /**
91 * Start the server (listen on the network for new connections).
92 * <p>
93 * Can only be called once.
94 * <p>
95 * This call is asynchronous, and will just start a new {@link Thread} on
96 * itself (see {@link WebLibraryServer#run()}).
97 */
98 public void start() {
99 new Thread(this).start();
100 }
101
09c2396e
NR
102 @Override
103 protected Response stop(WLoginResult login) {
104 if (!login.isRw()) {
105 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
106 NanoHTTPD.MIME_PLAINTEXT, "Exit not allowed");
107 }
108
109 if (exiting) {
110 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
111 NanoHTTPD.MIME_PLAINTEXT, "Server is already exiting...");
112 }
cab46763 113
09c2396e
NR
114 exiting = true;
115 Instance.getInstance().getTraceHandler().trace("Exiting");
116
117 boolean ok;
118 do {
119 synchronized (imprts) {
120 ok = imprts.isEmpty();
121 }
122 if (!ok) {
123 try {
124 Thread.sleep(2000);
125 } catch (InterruptedException e) {
126 Instance.getInstance().getTraceHandler()
127 .trace("Waiting to exit...");
128 }
129 }
130 } while (!ok);
131
132 doStop();
133
cab46763
NR
134 new Thread(new Runnable() {
135 @Override
136 public void run() {
137 try {
138 Thread.sleep(1500);
139 } catch (InterruptedException e) {
140 }
141
142 Instance.getInstance().getTraceHandler()
143 .trace("Exit timeout: force-quit");
144 System.exit(0);
145 }
146 }, "Exit program after timeout of 1500 ms").start();
147
09c2396e
NR
148 return NanoHTTPD.newFixedLengthResponse(Status.OK,
149 NanoHTTPD.MIME_PLAINTEXT, "Exited");
150 }
151
33d40b9f
NR
152 @Override
153 protected WLoginResult login(boolean badLogin, boolean badCookie) {
154 return new WLoginResult(false, false);
f433d153
NR
155 }
156
33d40b9f
NR
157 @Override
158 protected WLoginResult login(String who, String cookie) {
fce0a73f
NR
159 List<String> subkeys = Instance.getInstance().getConfig()
160 .getList(Config.SERVER_ALLOWED_SUBKEYS);
f433d153 161 String realKey = Instance.getInstance().getConfig()
fce0a73f 162 .getString(Config.SERVER_KEY);
d11fb35b 163
fce0a73f 164 return new WLoginResult(cookie, who, realKey, subkeys);
f433d153
NR
165 }
166
167 // allow rw/wl
33d40b9f
NR
168 @Override
169 protected WLoginResult login(String who, String key, String subkey) {
f433d153 170 String realKey = Instance.getInstance().getConfig()
d11fb35b 171 .getString(Config.SERVER_KEY, "");
f433d153
NR
172
173 // I don't like NULLs...
f433d153
NR
174 key = key == null ? "" : key;
175 subkey = subkey == null ? "" : subkey;
176
177 if (!realKey.equals(key)) {
fce0a73f 178 return new WLoginResult(true, false);
f433d153
NR
179 }
180
d11fb35b 181 // defaults are true (as previous versions without the feature)
f433d153
NR
182 boolean rw = true;
183 boolean wl = true;
d11fb35b 184 boolean bl = true;
f433d153 185
599b05c7
NR
186 rw = Instance.getInstance().getConfig().getBoolean(Config.SERVER_RW,
187 rw);
fce0a73f
NR
188
189 List<String> allowed = Instance.getInstance().getConfig().getList(
190 Config.SERVER_ALLOWED_SUBKEYS, new ArrayList<String>());
191
192 if (!allowed.isEmpty()) {
193 if (!allowed.contains(subkey)) {
194 return new WLoginResult(true, false);
195 }
196
197 if ((subkey + "|").contains("|rw|")) {
198 rw = true;
199 }
200 if ((subkey + "|").contains("|wl|")) {
201 wl = false; // |wl| = bypass whitelist
202 }
203 if ((subkey + "|").contains("|bl|")) {
204 bl = false; // |bl| = bypass blacklist
f433d153
NR
205 }
206 }
207
fce0a73f 208 return new WLoginResult(who, key, subkey, rw, wl, bl);
f433d153
NR
209 }
210
33d40b9f 211 @Override
fce0a73f 212 protected Response getList(String uri, WLoginResult login)
f433d153 213 throws IOException {
5ee0fc14 214 if (WebLibraryUrls.LIST_URL_METADATA.equals(uri)) {
f433d153 215 List<JSONObject> jsons = new ArrayList<JSONObject>();
d11fb35b 216 for (MetaData meta : metas(login)) {
f433d153
NR
217 jsons.add(JsonIO.toJson(meta));
218 }
219
220 return newInputStreamResponse("application/json",
599b05c7
NR
221 new ByteArrayInputStream(
222 new JSONArray(jsons).toString().getBytes()));
f433d153
NR
223 }
224
225 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
226 NanoHTTPD.MIME_PLAINTEXT, null);
227 }
228
f433d153
NR
229 // /story/luid/chapter/para <-- text/image
230 // /story/luid/cover <-- image
231 // /story/luid/metadata <-- json
c5103223 232 // /story/luid/json <-- json, whole chapter (no images)
33d40b9f
NR
233 @Override
234 protected Response getStoryPart(String uri, WLoginResult login) {
6673ec59 235 String[] uriParts = uri.split("/");
f433d153
NR
236 int off = 2;
237
6673ec59 238 if (uriParts.length < off + 2) {
f433d153
NR
239 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
240 NanoHTTPD.MIME_PLAINTEXT, null);
241 }
242
6673ec59
NR
243 String luid = uriParts[off + 0];
244 String chapterStr = uriParts[off + 1];
245 String imageStr = uriParts.length < off + 3 ? null : uriParts[off + 2];
f433d153
NR
246
247 // 1-based (0 = desc)
248 int chapter = 0;
249 if (chapterStr != null && !"cover".equals(chapterStr)
599b05c7
NR
250 && !"metadata".equals(chapterStr)
251 && !"json".equals(chapterStr)) {
f433d153
NR
252 try {
253 chapter = Integer.parseInt(chapterStr);
254 if (chapter < 0) {
255 throw new NumberFormatException();
256 }
257 } catch (NumberFormatException e) {
258 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
259 NanoHTTPD.MIME_PLAINTEXT, "Chapter is not valid");
260 }
261 }
262
263 // 1-based
264 int paragraph = 1;
265 if (imageStr != null) {
266 try {
267 paragraph = Integer.parseInt(imageStr);
268 if (paragraph < 0) {
269 throw new NumberFormatException();
270 }
271 } catch (NumberFormatException e) {
272 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
273 NanoHTTPD.MIME_PLAINTEXT, "Paragraph is not valid");
274 }
275 }
276
277 String mimeType = NanoHTTPD.MIME_PLAINTEXT;
278 InputStream in = null;
279 try {
280 if ("cover".equals(chapterStr)) {
6673ec59 281 Image img = storyCover(luid, login);
f433d153
NR
282 if (img != null) {
283 in = img.newInputStream();
284 }
3f468ac7
NR
285 // TODO: get correct image type
286 mimeType = "image/png";
f433d153 287 } else if ("metadata".equals(chapterStr)) {
d11fb35b 288 MetaData meta = meta(luid, login);
f433d153
NR
289 JSONObject json = JsonIO.toJson(meta);
290 mimeType = "application/json";
291 in = new ByteArrayInputStream(json.toString().getBytes());
3fbc084c 292 } else if ("json".equals(chapterStr)) {
d11fb35b 293 Story story = story(luid, login);
c5103223
NR
294 JSONObject json = JsonIO.toJson(story);
295 mimeType = "application/json";
296 in = new ByteArrayInputStream(json.toString().getBytes());
f433d153 297 } else {
d11fb35b 298 Story story = story(luid, login);
f433d153
NR
299 if (story != null) {
300 if (chapter == 0) {
301 StringBuilder builder = new StringBuilder();
302 for (Paragraph p : story.getMeta().getResume()) {
303 if (builder.length() == 0) {
304 builder.append("\n");
305 }
306 builder.append(p.getContent());
307 }
308
599b05c7
NR
309 in = new ByteArrayInputStream(
310 builder.toString().getBytes("utf-8"));
f433d153
NR
311 } else {
312 Paragraph para = story.getChapters().get(chapter - 1)
313 .getParagraphs().get(paragraph - 1);
314 Image img = para.getContentImage();
315 if (para.getType() == ParagraphType.IMAGE) {
316 // TODO: get correct image type
317 mimeType = "image/png";
318 in = img.newInputStream();
319 } else {
599b05c7
NR
320 in = new ByteArrayInputStream(
321 para.getContent().getBytes("utf-8"));
f433d153
NR
322 }
323 }
324 }
325 }
326 } catch (IndexOutOfBoundsException e) {
327 return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND,
328 NanoHTTPD.MIME_PLAINTEXT,
329 "Chapter or paragraph does not exist");
330 } catch (IOException e) {
331 Instance.getInstance().getTraceHandler()
332 .error(new IOException("Cannot get image: " + uri, e));
333 return NanoHTTPD.newFixedLengthResponse(Status.INTERNAL_ERROR,
334 NanoHTTPD.MIME_PLAINTEXT, "Error when processing request");
335 }
336
337 return newInputStreamResponse(mimeType, in);
338 }
339
089e354e
NR
340 // /story/luid/source
341 // /story/luid/title
342 // /story/luid/author
343 @Override
344 protected Response setStoryPart(String uri, String value,
345 WLoginResult login) throws IOException {
346 String[] uriParts = uri.split("/");
347 int off = 2; // "" and "story"
348
349 if (uriParts.length < off + 2) {
350 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
351 NanoHTTPD.MIME_PLAINTEXT, "Invalid story part request");
352 }
353
a1226ce0
NR
354 if (!login.isRw()) {
355 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
356 NanoHTTPD.MIME_PLAINTEXT, "SET story part not allowed");
357 }
cab46763 358
09c2396e
NR
359 if (exiting) {
360 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
361 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
362 }
a1226ce0 363
089e354e
NR
364 String luid = uriParts[off + 0];
365 String type = uriParts[off + 1];
366
367 if (!Arrays.asList("source", "title", "author").contains(type)) {
368 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
369 NanoHTTPD.MIME_PLAINTEXT,
370 "Invalid SET story part: " + type);
371 }
372
373 if (meta(luid, login) != null) {
374 BasicLibrary lib = Instance.getInstance().getLibrary();
375 if ("source".equals(type)) {
376 lib.changeSource(luid, value, null);
377 } else if ("title".equals(type)) {
378 lib.changeTitle(luid, value, null);
379 } else if ("author".equals(type)) {
380 lib.changeAuthor(luid, value, null);
381 }
382 }
383
384 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
385 }
386
6673ec59
NR
387 @Override
388 protected Response getCover(String uri, WLoginResult login)
389 throws IOException {
390 String[] uriParts = uri.split("/");
391 int off = 2; // "" and "cover"
392
393 if (uriParts.length < off + 2) {
394 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
395 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
396 }
397
398 String type = uriParts[off + 0];
399 String id = uriParts[off + 1];
400
401 InputStream in = null;
402
e4b1b70c 403 if ("story".equals(type)) {
6673ec59
NR
404 Image img = storyCover(id, login);
405 if (img != null) {
406 in = img.newInputStream();
407 }
e4b1b70c
NR
408 } else if ("source".equals(type)) {
409 Image img = sourceCover(id, login);
6673ec59
NR
410 if (img != null) {
411 in = img.newInputStream();
412 }
e4b1b70c
NR
413 } else if ("author".equals(type)) {
414 Image img = authorCover(id, login);
6673ec59
NR
415 if (img != null) {
416 in = img.newInputStream();
417 }
418 } else {
419 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
e4b1b70c
NR
420 NanoHTTPD.MIME_PLAINTEXT,
421 "Invalid GET cover type: " + type);
6673ec59
NR
422 }
423
424 // TODO: get correct image type
425 return newInputStreamResponse("image/png", in);
426 }
427
e4b1b70c
NR
428 @Override
429 protected Response setCover(String uri, String luid, WLoginResult login)
430 throws IOException {
431 String[] uriParts = uri.split("/");
432 int off = 2; // "" and "cover"
433
434 if (uriParts.length < off + 2) {
435 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
436 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
437 }
438
a1226ce0
NR
439 if (!login.isRw()) {
440 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
441 NanoHTTPD.MIME_PLAINTEXT, "Cover request not allowed");
442 }
cab46763 443
09c2396e
NR
444 if (exiting) {
445 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
446 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
447 }
a1226ce0 448
e4b1b70c
NR
449 String type = uriParts[off + 0];
450 String id = uriParts[off + 1];
451
452 if ("source".equals(type)) {
453 sourceCover(id, login, luid);
454 } else if ("author".equals(type)) {
455 authorCover(id, login, luid);
456 } else {
457 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
458 NanoHTTPD.MIME_PLAINTEXT,
459 "Invalid SET cover type: " + type);
460 }
461
462 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
463 }
464
f70bcacf
NR
465 @Override
466 protected Response imprt(String uri, String urlStr, WLoginResult login)
467 throws IOException {
468 final BasicLibrary lib = Instance.getInstance().getLibrary();
469
a1226ce0
NR
470 if (!login.isRw()) {
471 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
472 NanoHTTPD.MIME_PLAINTEXT, "Import not allowed");
473 }
cab46763 474
09c2396e
NR
475 if (exiting) {
476 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
477 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
478 }
a1226ce0 479
f70bcacf
NR
480 final URL url = new URL(urlStr);
481 final Progress pg = new Progress();
482 final String luid = lib.getNextId();
483
484 synchronized (imprts) {
485 imprts.put(luid, pg);
486 }
487
488 new Thread(new Runnable() {
489 @Override
490 public void run() {
491 try {
acbec0d2 492 lib.imprt(url, luid, pg);
f70bcacf
NR
493 } catch (IOException e) {
494 Instance.getInstance().getTraceHandler().error(e);
495 } finally {
496 synchronized (imprts) {
497 imprts.remove(luid);
498 }
499 }
500 }
501 }, "Import story: " + urlStr).start();
502
f70bcacf
NR
503 return NanoHTTPD.newFixedLengthResponse(Status.OK,
504 NanoHTTPD.MIME_PLAINTEXT, luid);
505 }
506
507 @Override
508 protected Response imprtProgress(String uri, WLoginResult login) {
509 String[] uriParts = uri.split("/");
510 int off = 2; // "" and "import"
511
512 if (uriParts.length < off + 1) {
513 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
514 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
515 }
516
517 String luid = uriParts[off + 0];
518
519 Progress pg = null;
520 synchronized (imprts) {
521 pg = imprts.get(luid);
522 }
523 if (pg != null) {
524 return NanoHTTPD.newFixedLengthResponse(Status.OK,
525 "application/json", JsonIO.toJson(pg).toString());
526 }
527
528 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
529 }
530
a1226ce0
NR
531 @Override
532 protected Response delete(String uri, WLoginResult login)
533 throws IOException {
534 String[] uriParts = uri.split("/");
535 int off = 2; // "" and "delete"
536
537 if (uriParts.length < off + 1) {
538 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
539 NanoHTTPD.MIME_PLAINTEXT, "Invalid delete request");
540 }
541
542 if (!login.isRw()) {
543 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
544 NanoHTTPD.MIME_PLAINTEXT, "Delete not allowed");
545 }
cab46763 546
09c2396e
NR
547 if (exiting) {
548 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
549 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
550 }
a1226ce0
NR
551
552 String luid = uriParts[off + 0];
553
554 BasicLibrary lib = Instance.getInstance().getLibrary();
555 lib.delete(luid);
556
557 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
558 }
559
33d40b9f
NR
560 @Override
561 protected List<MetaData> metas(WLoginResult login) throws IOException {
d11fb35b
NR
562 BasicLibrary lib = Instance.getInstance().getLibrary();
563 List<MetaData> metas = new ArrayList<MetaData>();
564 for (MetaData meta : lib.getList().getMetas()) {
565 if (isAllowed(meta, login)) {
566 metas.add(meta);
567 }
568 }
569
570 return metas;
571 }
572
f433d153 573 // NULL if not whitelist OK or if not found
33d40b9f
NR
574 @Override
575 protected Story story(String luid, WLoginResult login) throws IOException {
f433d153
NR
576 synchronized (storyCache) {
577 if (storyCache.containsKey(luid)) {
578 Story story = storyCache.get(luid);
d11fb35b 579 if (!isAllowed(story.getMeta(), login))
f433d153 580 return null;
f433d153
NR
581
582 return story;
583 }
584 }
585
586 Story story = null;
d11fb35b 587 MetaData meta = meta(luid, login);
f433d153
NR
588 if (meta != null) {
589 BasicLibrary lib = Instance.getInstance().getLibrary();
590 story = lib.getStory(luid, null);
591 long size = sizeOf(story);
592
593 synchronized (storyCache) {
594 // Could have been added by another request
595 if (!storyCache.containsKey(luid)) {
596 while (!storyCacheOrder.isEmpty()
597 && storyCacheSize + size > maxStoryCacheSize) {
598 String oldestLuid = storyCacheOrder.removeFirst();
599 Story oldestStory = storyCache.remove(oldestLuid);
600 maxStoryCacheSize -= sizeOf(oldestStory);
601 }
602
603 storyCacheOrder.add(luid);
604 storyCache.put(luid, story);
605 }
606 }
607 }
608
609 return story;
610 }
611
33d40b9f
NR
612 private MetaData meta(String luid, WLoginResult login) throws IOException {
613 BasicLibrary lib = Instance.getInstance().getLibrary();
614 MetaData meta = lib.getInfo(luid);
615 if (!isAllowed(meta, login))
616 return null;
f433d153 617
33d40b9f 618 return meta;
f433d153
NR
619 }
620
6673ec59
NR
621 private Image storyCover(String luid, WLoginResult login)
622 throws IOException {
33d40b9f
NR
623 MetaData meta = meta(luid, login);
624 if (meta != null) {
625 BasicLibrary lib = Instance.getInstance().getLibrary();
626 return lib.getCover(meta.getLuid());
f433d153 627 }
f433d153 628
33d40b9f 629 return null;
f433d153
NR
630 }
631
6673ec59
NR
632 private Image authorCover(String author, WLoginResult login)
633 throws IOException {
634 Image img = null;
635
636 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
637 author, null);
638 if (metas.size() > 0) {
639 BasicLibrary lib = Instance.getInstance().getLibrary();
640 img = lib.getCustomAuthorCover(author);
641 if (img == null)
642 img = lib.getCover(metas.get(0).getLuid());
643 }
644
645 return img;
646
647 }
648
e4b1b70c
NR
649 private void authorCover(String author, WLoginResult login, String luid)
650 throws IOException {
651 if (meta(luid, login) != null) {
652 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
653 author, null);
654 if (metas.size() > 0) {
655 BasicLibrary lib = Instance.getInstance().getLibrary();
656 lib.setAuthorCover(author, luid);
657 }
658 }
659 }
660
6673ec59
NR
661 private Image sourceCover(String source, WLoginResult login)
662 throws IOException {
663 Image img = null;
664
665 List<MetaData> metas = new MetaResultList(metas(login)).filter(source,
666 null, null);
667 if (metas.size() > 0) {
668 BasicLibrary lib = Instance.getInstance().getLibrary();
669 img = lib.getCustomSourceCover(source);
670 if (img == null)
671 img = lib.getCover(metas.get(0).getLuid());
672 }
673
674 return img;
675 }
676
e4b1b70c
NR
677 private void sourceCover(String source, WLoginResult login, String luid)
678 throws IOException {
679 if (meta(luid, login) != null) {
680 List<MetaData> metas = new MetaResultList(metas(login))
681 .filter(source, null, null);
682 if (metas.size() > 0) {
683 BasicLibrary lib = Instance.getInstance().getLibrary();
684 lib.setSourceCover(source, luid);
685 }
686 }
687 }
688
33d40b9f 689 private boolean isAllowed(MetaData meta, WLoginResult login) {
5c4ce687
NR
690 MetaResultList one = new MetaResultList(Arrays.asList(meta));
691 if (login.isWl() && !whitelist.isEmpty()) {
692 if (one.filter(whitelist, null, null).isEmpty()) {
693 return false;
694 }
f433d153 695 }
5c4ce687
NR
696 if (login.isBl() && !blacklist.isEmpty()) {
697 if (!one.filter(blacklist, null, null).isEmpty()) {
698 return false;
699 }
599b05c7
NR
700 }
701
33d40b9f 702 return true;
599b05c7
NR
703 }
704
33d40b9f
NR
705 private long sizeOf(Story story) {
706 long size = 0;
707 for (Chapter chap : story) {
708 for (Paragraph para : chap) {
709 if (para.getType() == ParagraphType.IMAGE) {
710 size += para.getContentImage().getSize();
711 } else {
712 size += para.getContent().length();
713 }
714 }
6b89e45c
NR
715 }
716
33d40b9f 717 return size;
6b89e45c
NR
718 }
719
3fbc084c
NR
720 public static void main(String[] args) throws IOException {
721 Instance.init();
722 WebLibraryServer web = new WebLibraryServer(false);
723 web.run();
724 }
f433d153 725}