update to master
[fanfix.git] / 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;
4536c5cf 6import java.net.URL;
f433d153 7import java.util.ArrayList;
4536c5cf 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;
c91f1830 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;
4536c5cf 30import be.nikiroo.utils.Progress;
f433d153 31
c91f1830
NR
32public class WebLibraryServer extends WebLibraryServerHtml {
33 class WLoginResult extends LoginResult {
c91f1830
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
c91f1830 44 public WLoginResult(String cookie, String who, String key,
f433d153 45 List<String> subkeys) {
c91f1830
NR
46 super(cookie, who, key, subkeys,
47 subkeys == null || subkeys.isEmpty());
f433d153
NR
48 }
49
50 public boolean isRw() {
c91f1830 51 return getOption().contains("|rw|");
f433d153
NR
52 }
53
54 public boolean isWl() {
c91f1830 55 return getOption().contains("|wl|");
f433d153
NR
56 }
57
c91f1830
NR
58 public boolean isBl() {
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;
c91f1830
NR
67
68 private List<String> whitelist;
69 private List<String> blacklist;
f433d153 70
4536c5cf
NR
71 private Map<String, Progress> imprts = new HashMap<String, Progress>();
72
6d465e88
NR
73 private boolean exiting;
74
f433d153 75 public WebLibraryServer(boolean secure) throws IOException {
c91f1830 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
c91f1830
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
6d465e88
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 }
002972e9 113
6d465e88
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
002972e9
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
6d465e88
NR
148 return NanoHTTPD.newFixedLengthResponse(Status.OK,
149 NanoHTTPD.MIME_PLAINTEXT, "Exited");
150 }
151
c91f1830
NR
152 @Override
153 protected WLoginResult login(boolean badLogin, boolean badCookie) {
154 return new WLoginResult(false, false);
f433d153
NR
155 }
156
c91f1830
NR
157 @Override
158 protected WLoginResult login(String who, String cookie) {
159 List<String> subkeys = Instance.getInstance().getConfig()
160 .getList(Config.SERVER_ALLOWED_SUBKEYS);
f433d153
NR
161 String realKey = Instance.getInstance().getConfig()
162 .getString(Config.SERVER_KEY);
c91f1830
NR
163
164 return new WLoginResult(cookie, who, realKey, subkeys);
f433d153
NR
165 }
166
167 // allow rw/wl
c91f1830
NR
168 @Override
169 protected WLoginResult login(String who, String key, String subkey) {
f433d153 170 String realKey = Instance.getInstance().getConfig()
c91f1830 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)) {
c91f1830 178 return new WLoginResult(true, false);
f433d153
NR
179 }
180
c91f1830 181 // defaults are true (as previous versions without the feature)
f433d153
NR
182 boolean rw = true;
183 boolean wl = true;
c91f1830 184 boolean bl = true;
f433d153
NR
185
186 rw = Instance.getInstance().getConfig().getBoolean(Config.SERVER_RW,
187 rw);
f433d153 188
c91f1830
NR
189 List<String> allowed = Instance.getInstance().getConfig().getList(
190 Config.SERVER_ALLOWED_SUBKEYS, new ArrayList<String>());
f433d153 191
c91f1830
NR
192 if (!allowed.isEmpty()) {
193 if (!allowed.contains(subkey)) {
194 return new WLoginResult(true, false);
195 }
f433d153 196
c91f1830
NR
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
205 }
f433d153
NR
206 }
207
c91f1830 208 return new WLoginResult(who, key, subkey, rw, wl, bl);
f433d153
NR
209 }
210
c91f1830
NR
211 @Override
212 protected Response getList(String uri, WLoginResult login)
f433d153 213 throws IOException {
c91f1830 214 if (WebLibraryUrls.LIST_URL_METADATA.equals(uri)) {
f433d153 215 List<JSONObject> jsons = new ArrayList<JSONObject>();
c91f1830 216 for (MetaData meta : metas(login)) {
f433d153
NR
217 jsons.add(JsonIO.toJson(meta));
218 }
219
379a497e
NR
220 return NanoHTTPD.newFixedLengthResponse(Status.OK,
221 "application/json", new JSONArray(jsons).toString());
f433d153
NR
222 }
223
224 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
225 NanoHTTPD.MIME_PLAINTEXT, null);
226 }
227
f433d153
NR
228 // /story/luid/chapter/para <-- text/image
229 // /story/luid/cover <-- image
230 // /story/luid/metadata <-- json
4b3d19dc 231 // /story/luid/json <-- json, whole chapter (no images)
c91f1830
NR
232 @Override
233 protected Response getStoryPart(String uri, WLoginResult login) {
4536c5cf 234 String[] uriParts = uri.split("/");
f433d153
NR
235 int off = 2;
236
4536c5cf 237 if (uriParts.length < off + 2) {
f433d153
NR
238 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
239 NanoHTTPD.MIME_PLAINTEXT, null);
240 }
241
4536c5cf
NR
242 String luid = uriParts[off + 0];
243 String chapterStr = uriParts[off + 1];
244 String imageStr = uriParts.length < off + 3 ? null : uriParts[off + 2];
f433d153
NR
245
246 // 1-based (0 = desc)
247 int chapter = 0;
248 if (chapterStr != null && !"cover".equals(chapterStr)
b459e462
NR
249 && !"metadata".equals(chapterStr)
250 && !"json".equals(chapterStr)) {
f433d153
NR
251 try {
252 chapter = Integer.parseInt(chapterStr);
253 if (chapter < 0) {
254 throw new NumberFormatException();
255 }
256 } catch (NumberFormatException e) {
257 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
258 NanoHTTPD.MIME_PLAINTEXT, "Chapter is not valid");
259 }
260 }
261
262 // 1-based
263 int paragraph = 1;
264 if (imageStr != null) {
265 try {
266 paragraph = Integer.parseInt(imageStr);
267 if (paragraph < 0) {
268 throw new NumberFormatException();
269 }
270 } catch (NumberFormatException e) {
271 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
272 NanoHTTPD.MIME_PLAINTEXT, "Paragraph is not valid");
273 }
274 }
275
276 String mimeType = NanoHTTPD.MIME_PLAINTEXT;
277 InputStream in = null;
278 try {
279 if ("cover".equals(chapterStr)) {
4536c5cf 280 Image img = storyCover(luid, login);
f433d153
NR
281 if (img != null) {
282 in = img.newInputStream();
283 }
b459e462
NR
284 // TODO: get correct image type
285 mimeType = "image/png";
f433d153 286 } else if ("metadata".equals(chapterStr)) {
c91f1830 287 MetaData meta = meta(luid, login);
f433d153
NR
288 JSONObject json = JsonIO.toJson(meta);
289 mimeType = "application/json";
290 in = new ByteArrayInputStream(json.toString().getBytes());
b459e462 291 } else if ("json".equals(chapterStr)) {
c91f1830 292 Story story = story(luid, login);
4b3d19dc
NR
293 JSONObject json = JsonIO.toJson(story);
294 mimeType = "application/json";
295 in = new ByteArrayInputStream(json.toString().getBytes());
f433d153 296 } else {
c91f1830 297 Story story = story(luid, login);
f433d153
NR
298 if (story != null) {
299 if (chapter == 0) {
300 StringBuilder builder = new StringBuilder();
301 for (Paragraph p : story.getMeta().getResume()) {
302 if (builder.length() == 0) {
303 builder.append("\n");
304 }
305 builder.append(p.getContent());
306 }
307
308 in = new ByteArrayInputStream(
309 builder.toString().getBytes("utf-8"));
310 } else {
311 Paragraph para = story.getChapters().get(chapter - 1)
312 .getParagraphs().get(paragraph - 1);
313 Image img = para.getContentImage();
314 if (para.getType() == ParagraphType.IMAGE) {
315 // TODO: get correct image type
316 mimeType = "image/png";
317 in = img.newInputStream();
318 } else {
319 in = new ByteArrayInputStream(
320 para.getContent().getBytes("utf-8"));
321 }
322 }
323 }
324 }
325 } catch (IndexOutOfBoundsException e) {
326 return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND,
327 NanoHTTPD.MIME_PLAINTEXT,
328 "Chapter or paragraph does not exist");
329 } catch (IOException e) {
330 Instance.getInstance().getTraceHandler()
331 .error(new IOException("Cannot get image: " + uri, e));
332 return NanoHTTPD.newFixedLengthResponse(Status.INTERNAL_ERROR,
333 NanoHTTPD.MIME_PLAINTEXT, "Error when processing request");
334 }
335
336 return newInputStreamResponse(mimeType, in);
337 }
338
4536c5cf
NR
339 // /story/luid/source
340 // /story/luid/title
341 // /story/luid/author
342 @Override
343 protected Response setStoryPart(String uri, String value,
344 WLoginResult login) throws IOException {
345 String[] uriParts = uri.split("/");
346 int off = 2; // "" and "story"
347
348 if (uriParts.length < off + 2) {
349 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
350 NanoHTTPD.MIME_PLAINTEXT, "Invalid story part request");
351 }
352
159e970c
NR
353 if (!login.isRw()) {
354 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
355 NanoHTTPD.MIME_PLAINTEXT, "SET story part not allowed");
356 }
002972e9 357
6d465e88
NR
358 if (exiting) {
359 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
360 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
361 }
159e970c 362
4536c5cf
NR
363 String luid = uriParts[off + 0];
364 String type = uriParts[off + 1];
365
366 if (!Arrays.asList("source", "title", "author").contains(type)) {
367 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
368 NanoHTTPD.MIME_PLAINTEXT,
369 "Invalid SET story part: " + type);
370 }
371
372 if (meta(luid, login) != null) {
373 BasicLibrary lib = Instance.getInstance().getLibrary();
374 if ("source".equals(type)) {
375 lib.changeSource(luid, value, null);
376 } else if ("title".equals(type)) {
377 lib.changeTitle(luid, value, null);
378 } else if ("author".equals(type)) {
379 lib.changeAuthor(luid, value, null);
380 }
381 }
382
383 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
384 }
385
386 @Override
387 protected Response getCover(String uri, WLoginResult login)
388 throws IOException {
389 String[] uriParts = uri.split("/");
390 int off = 2; // "" and "cover"
391
392 if (uriParts.length < off + 2) {
393 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
394 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
395 }
396
397 String type = uriParts[off + 0];
398 String id = uriParts[off + 1];
399
400 InputStream in = null;
401
402 if ("story".equals(type)) {
403 Image img = storyCover(id, login);
404 if (img != null) {
405 in = img.newInputStream();
406 }
407 } else if ("source".equals(type)) {
408 Image img = sourceCover(id, login);
409 if (img != null) {
410 in = img.newInputStream();
411 }
412 } else if ("author".equals(type)) {
413 Image img = authorCover(id, login);
414 if (img != null) {
415 in = img.newInputStream();
416 }
417 } else {
418 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
419 NanoHTTPD.MIME_PLAINTEXT,
420 "Invalid GET cover type: " + type);
421 }
422
423 // TODO: get correct image type
424 return newInputStreamResponse("image/png", in);
425 }
426
427 @Override
428 protected Response setCover(String uri, String luid, WLoginResult login)
429 throws IOException {
430 String[] uriParts = uri.split("/");
431 int off = 2; // "" and "cover"
432
433 if (uriParts.length < off + 2) {
434 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
435 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
436 }
437
159e970c
NR
438 if (!login.isRw()) {
439 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
440 NanoHTTPD.MIME_PLAINTEXT, "Cover request not allowed");
441 }
002972e9 442
6d465e88
NR
443 if (exiting) {
444 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
445 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
446 }
159e970c 447
4536c5cf
NR
448 String type = uriParts[off + 0];
449 String id = uriParts[off + 1];
450
451 if ("source".equals(type)) {
452 sourceCover(id, login, luid);
453 } else if ("author".equals(type)) {
454 authorCover(id, login, luid);
455 } else {
456 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
457 NanoHTTPD.MIME_PLAINTEXT,
458 "Invalid SET cover type: " + type);
459 }
460
461 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
462 }
463
464 @Override
465 protected Response imprt(String uri, String urlStr, WLoginResult login)
466 throws IOException {
467 final BasicLibrary lib = Instance.getInstance().getLibrary();
468
159e970c
NR
469 if (!login.isRw()) {
470 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
471 NanoHTTPD.MIME_PLAINTEXT, "Import not allowed");
472 }
002972e9 473
6d465e88
NR
474 if (exiting) {
475 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
476 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
477 }
159e970c 478
4536c5cf
NR
479 final URL url = new URL(urlStr);
480 final Progress pg = new Progress();
481 final String luid = lib.getNextId();
482
483 synchronized (imprts) {
484 imprts.put(luid, pg);
485 }
486
487 new Thread(new Runnable() {
488 @Override
489 public void run() {
490 try {
cd1aa392 491 lib.imprt(url, luid, pg);
4536c5cf
NR
492 } catch (IOException e) {
493 Instance.getInstance().getTraceHandler().error(e);
494 } finally {
495 synchronized (imprts) {
496 imprts.remove(luid);
497 }
498 }
499 }
500 }, "Import story: " + urlStr).start();
501
4536c5cf
NR
502 return NanoHTTPD.newFixedLengthResponse(Status.OK,
503 NanoHTTPD.MIME_PLAINTEXT, luid);
504 }
505
506 @Override
507 protected Response imprtProgress(String uri, WLoginResult login) {
508 String[] uriParts = uri.split("/");
509 int off = 2; // "" and "import"
510
511 if (uriParts.length < off + 1) {
512 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
513 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
514 }
515
516 String luid = uriParts[off + 0];
517
518 Progress pg = null;
519 synchronized (imprts) {
520 pg = imprts.get(luid);
521 }
522 if (pg != null) {
523 return NanoHTTPD.newFixedLengthResponse(Status.OK,
524 "application/json", JsonIO.toJson(pg).toString());
525 }
526
527 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
528 }
529
159e970c
NR
530 @Override
531 protected Response delete(String uri, WLoginResult login)
532 throws IOException {
533 String[] uriParts = uri.split("/");
534 int off = 2; // "" and "delete"
535
536 if (uriParts.length < off + 1) {
537 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
538 NanoHTTPD.MIME_PLAINTEXT, "Invalid delete request");
539 }
540
541 if (!login.isRw()) {
542 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
543 NanoHTTPD.MIME_PLAINTEXT, "Delete not allowed");
544 }
002972e9 545
6d465e88
NR
546 if (exiting) {
547 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
548 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
549 }
159e970c
NR
550
551 String luid = uriParts[off + 0];
552
553 BasicLibrary lib = Instance.getInstance().getLibrary();
554 lib.delete(luid);
555
556 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
557 }
558
c91f1830
NR
559 @Override
560 protected List<MetaData> metas(WLoginResult login) throws IOException {
f433d153 561 BasicLibrary lib = Instance.getInstance().getLibrary();
c91f1830
NR
562 List<MetaData> metas = new ArrayList<MetaData>();
563 for (MetaData meta : lib.getList().getMetas()) {
564 if (isAllowed(meta, login)) {
565 metas.add(meta);
566 }
f433d153
NR
567 }
568
c91f1830 569 return metas;
f433d153
NR
570 }
571
572 // NULL if not whitelist OK or if not found
c91f1830
NR
573 @Override
574 protected Story story(String luid, WLoginResult login) throws IOException {
f433d153
NR
575 synchronized (storyCache) {
576 if (storyCache.containsKey(luid)) {
577 Story story = storyCache.get(luid);
c91f1830 578 if (!isAllowed(story.getMeta(), login))
f433d153 579 return null;
f433d153
NR
580
581 return story;
582 }
583 }
584
585 Story story = null;
c91f1830 586 MetaData meta = meta(luid, login);
f433d153
NR
587 if (meta != null) {
588 BasicLibrary lib = Instance.getInstance().getLibrary();
589 story = lib.getStory(luid, null);
590 long size = sizeOf(story);
591
592 synchronized (storyCache) {
593 // Could have been added by another request
594 if (!storyCache.containsKey(luid)) {
595 while (!storyCacheOrder.isEmpty()
596 && storyCacheSize + size > maxStoryCacheSize) {
597 String oldestLuid = storyCacheOrder.removeFirst();
598 Story oldestStory = storyCache.remove(oldestLuid);
599 maxStoryCacheSize -= sizeOf(oldestStory);
600 }
601
602 storyCacheOrder.add(luid);
603 storyCache.put(luid, story);
604 }
605 }
606 }
607
608 return story;
609 }
610
c91f1830
NR
611 private MetaData meta(String luid, WLoginResult login) throws IOException {
612 BasicLibrary lib = Instance.getInstance().getLibrary();
613 MetaData meta = lib.getInfo(luid);
614 if (!isAllowed(meta, login))
615 return null;
f433d153 616
c91f1830 617 return meta;
f433d153
NR
618 }
619
4536c5cf
NR
620 private Image storyCover(String luid, WLoginResult login)
621 throws IOException {
c91f1830
NR
622 MetaData meta = meta(luid, login);
623 if (meta != null) {
624 BasicLibrary lib = Instance.getInstance().getLibrary();
625 return lib.getCover(meta.getLuid());
f433d153 626 }
f433d153 627
c91f1830 628 return null;
f433d153
NR
629 }
630
4536c5cf
NR
631 private Image authorCover(String author, WLoginResult login)
632 throws IOException {
633 Image img = null;
634
635 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
636 author, null);
637 if (metas.size() > 0) {
638 BasicLibrary lib = Instance.getInstance().getLibrary();
639 img = lib.getCustomAuthorCover(author);
640 if (img == null)
641 img = lib.getCover(metas.get(0).getLuid());
642 }
643
644 return img;
645
646 }
647
648 private void authorCover(String author, WLoginResult login, String luid)
649 throws IOException {
650 if (meta(luid, login) != null) {
651 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
652 author, null);
653 if (metas.size() > 0) {
654 BasicLibrary lib = Instance.getInstance().getLibrary();
655 lib.setAuthorCover(author, luid);
656 }
657 }
658 }
659
660 private Image sourceCover(String source, WLoginResult login)
661 throws IOException {
662 Image img = null;
663
664 List<MetaData> metas = new MetaResultList(metas(login)).filter(source,
665 null, null);
666 if (metas.size() > 0) {
667 BasicLibrary lib = Instance.getInstance().getLibrary();
668 img = lib.getCustomSourceCover(source);
669 if (img == null)
670 img = lib.getCover(metas.get(0).getLuid());
671 }
672
673 return img;
674 }
675
676 private void sourceCover(String source, WLoginResult login, String luid)
677 throws IOException {
678 if (meta(luid, login) != null) {
679 List<MetaData> metas = new MetaResultList(metas(login))
680 .filter(source, null, null);
681 if (metas.size() > 0) {
682 BasicLibrary lib = Instance.getInstance().getLibrary();
683 lib.setSourceCover(source, luid);
684 }
685 }
686 }
687
c91f1830 688 private boolean isAllowed(MetaData meta, WLoginResult login) {
4536c5cf
NR
689 MetaResultList one = new MetaResultList(Arrays.asList(meta));
690 if (login.isWl() && !whitelist.isEmpty()) {
691 if (one.filter(whitelist, null, null).isEmpty()) {
692 return false;
693 }
f433d153 694 }
4536c5cf
NR
695 if (login.isBl() && !blacklist.isEmpty()) {
696 if (!one.filter(blacklist, null, null).isEmpty()) {
697 return false;
698 }
b459e462
NR
699 }
700
c91f1830 701 return true;
b459e462
NR
702 }
703
c91f1830
NR
704 private long sizeOf(Story story) {
705 long size = 0;
706 for (Chapter chap : story) {
707 for (Paragraph para : chap) {
708 if (para.getType() == ParagraphType.IMAGE) {
709 size += para.getContentImage().getSize();
710 } else {
711 size += para.getContent().length();
712 }
713 }
b459e462
NR
714 }
715
c91f1830 716 return size;
b459e462
NR
717 }
718
719 public static void main(String[] args) throws IOException {
720 Instance.init();
721 WebLibraryServer web = new WebLibraryServer(false);
722 web.run();
723 }
f433d153 724}