update from 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("/");
bb7d9ea9 390 int startAt = 0;
4536c5cf 391 int off = 2; // "" and "cover"
bb7d9ea9
NR
392 for (int i = 0; i < off; i++) {
393 startAt += uriParts[i].length() + "/".length();
394 }
4536c5cf
NR
395
396 if (uriParts.length < off + 2) {
397 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
398 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
399 }
400
401 String type = uriParts[off + 0];
bb7d9ea9 402 String id = uri.substring(startAt + type.length() + "/".length());
4536c5cf
NR
403
404 InputStream in = null;
405
406 if ("story".equals(type)) {
407 Image img = storyCover(id, login);
408 if (img != null) {
409 in = img.newInputStream();
410 }
411 } else if ("source".equals(type)) {
412 Image img = sourceCover(id, login);
413 if (img != null) {
414 in = img.newInputStream();
415 }
416 } else if ("author".equals(type)) {
417 Image img = authorCover(id, login);
418 if (img != null) {
419 in = img.newInputStream();
420 }
421 } else {
422 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
423 NanoHTTPD.MIME_PLAINTEXT,
424 "Invalid GET cover type: " + type);
425 }
426
427 // TODO: get correct image type
428 return newInputStreamResponse("image/png", in);
429 }
430
431 @Override
432 protected Response setCover(String uri, String luid, WLoginResult login)
433 throws IOException {
434 String[] uriParts = uri.split("/");
435 int off = 2; // "" and "cover"
bb7d9ea9
NR
436 int startAt = 0;
437 for (int i = 0; i < off; i++) {
438 startAt += uriParts[i].length() + "/".length();
439 }
4536c5cf
NR
440
441 if (uriParts.length < off + 2) {
442 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
443 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
444 }
445
159e970c
NR
446 if (!login.isRw()) {
447 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
448 NanoHTTPD.MIME_PLAINTEXT, "Cover request not allowed");
449 }
002972e9 450
6d465e88
NR
451 if (exiting) {
452 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
453 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
454 }
159e970c 455
4536c5cf 456 String type = uriParts[off + 0];
bb7d9ea9 457 String id = uri.substring(startAt + type.length() + "/".length());
4536c5cf
NR
458
459 if ("source".equals(type)) {
460 sourceCover(id, login, luid);
461 } else if ("author".equals(type)) {
462 authorCover(id, login, luid);
463 } else {
464 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
465 NanoHTTPD.MIME_PLAINTEXT,
466 "Invalid SET cover type: " + type);
467 }
468
469 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
470 }
471
472 @Override
473 protected Response imprt(String uri, String urlStr, WLoginResult login)
474 throws IOException {
475 final BasicLibrary lib = Instance.getInstance().getLibrary();
476
159e970c
NR
477 if (!login.isRw()) {
478 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
479 NanoHTTPD.MIME_PLAINTEXT, "Import not allowed");
480 }
002972e9 481
6d465e88
NR
482 if (exiting) {
483 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
484 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
485 }
159e970c 486
4536c5cf
NR
487 final URL url = new URL(urlStr);
488 final Progress pg = new Progress();
489 final String luid = lib.getNextId();
490
491 synchronized (imprts) {
492 imprts.put(luid, pg);
493 }
494
495 new Thread(new Runnable() {
496 @Override
497 public void run() {
498 try {
cd1aa392 499 lib.imprt(url, luid, pg);
4536c5cf
NR
500 } catch (IOException e) {
501 Instance.getInstance().getTraceHandler().error(e);
502 } finally {
503 synchronized (imprts) {
504 imprts.remove(luid);
505 }
506 }
507 }
508 }, "Import story: " + urlStr).start();
509
4536c5cf
NR
510 return NanoHTTPD.newFixedLengthResponse(Status.OK,
511 NanoHTTPD.MIME_PLAINTEXT, luid);
512 }
513
514 @Override
515 protected Response imprtProgress(String uri, WLoginResult login) {
516 String[] uriParts = uri.split("/");
517 int off = 2; // "" and "import"
518
519 if (uriParts.length < off + 1) {
520 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
521 NanoHTTPD.MIME_PLAINTEXT, "Invalid cover request");
522 }
523
524 String luid = uriParts[off + 0];
525
526 Progress pg = null;
527 synchronized (imprts) {
528 pg = imprts.get(luid);
529 }
530 if (pg != null) {
531 return NanoHTTPD.newFixedLengthResponse(Status.OK,
532 "application/json", JsonIO.toJson(pg).toString());
533 }
534
535 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
536 }
537
159e970c
NR
538 @Override
539 protected Response delete(String uri, WLoginResult login)
540 throws IOException {
541 String[] uriParts = uri.split("/");
542 int off = 2; // "" and "delete"
543
544 if (uriParts.length < off + 1) {
545 return NanoHTTPD.newFixedLengthResponse(Status.BAD_REQUEST,
546 NanoHTTPD.MIME_PLAINTEXT, "Invalid delete request");
547 }
548
549 if (!login.isRw()) {
550 return NanoHTTPD.newFixedLengthResponse(Status.FORBIDDEN,
551 NanoHTTPD.MIME_PLAINTEXT, "Delete not allowed");
552 }
002972e9 553
6d465e88
NR
554 if (exiting) {
555 return NanoHTTPD.newFixedLengthResponse(Status.SERVICE_UNAVAILABLE,
556 NanoHTTPD.MIME_PLAINTEXT, "Server is exiting...");
557 }
159e970c
NR
558
559 String luid = uriParts[off + 0];
560
561 BasicLibrary lib = Instance.getInstance().getLibrary();
562 lib.delete(luid);
563
564 return newInputStreamResponse(NanoHTTPD.MIME_PLAINTEXT, null);
565 }
566
c91f1830
NR
567 @Override
568 protected List<MetaData> metas(WLoginResult login) throws IOException {
f433d153 569 BasicLibrary lib = Instance.getInstance().getLibrary();
c91f1830
NR
570 List<MetaData> metas = new ArrayList<MetaData>();
571 for (MetaData meta : lib.getList().getMetas()) {
572 if (isAllowed(meta, login)) {
573 metas.add(meta);
574 }
f433d153
NR
575 }
576
c91f1830 577 return metas;
f433d153
NR
578 }
579
580 // NULL if not whitelist OK or if not found
c91f1830
NR
581 @Override
582 protected Story story(String luid, WLoginResult login) throws IOException {
f433d153
NR
583 synchronized (storyCache) {
584 if (storyCache.containsKey(luid)) {
585 Story story = storyCache.get(luid);
c91f1830 586 if (!isAllowed(story.getMeta(), login))
f433d153 587 return null;
f433d153
NR
588
589 return story;
590 }
591 }
592
593 Story story = null;
c91f1830 594 MetaData meta = meta(luid, login);
f433d153
NR
595 if (meta != null) {
596 BasicLibrary lib = Instance.getInstance().getLibrary();
597 story = lib.getStory(luid, null);
598 long size = sizeOf(story);
599
600 synchronized (storyCache) {
601 // Could have been added by another request
602 if (!storyCache.containsKey(luid)) {
603 while (!storyCacheOrder.isEmpty()
604 && storyCacheSize + size > maxStoryCacheSize) {
605 String oldestLuid = storyCacheOrder.removeFirst();
606 Story oldestStory = storyCache.remove(oldestLuid);
607 maxStoryCacheSize -= sizeOf(oldestStory);
608 }
609
610 storyCacheOrder.add(luid);
611 storyCache.put(luid, story);
612 }
613 }
614 }
615
616 return story;
617 }
618
c91f1830
NR
619 private MetaData meta(String luid, WLoginResult login) throws IOException {
620 BasicLibrary lib = Instance.getInstance().getLibrary();
621 MetaData meta = lib.getInfo(luid);
622 if (!isAllowed(meta, login))
623 return null;
f433d153 624
c91f1830 625 return meta;
f433d153
NR
626 }
627
4536c5cf
NR
628 private Image storyCover(String luid, WLoginResult login)
629 throws IOException {
c91f1830
NR
630 MetaData meta = meta(luid, login);
631 if (meta != null) {
632 BasicLibrary lib = Instance.getInstance().getLibrary();
633 return lib.getCover(meta.getLuid());
f433d153 634 }
f433d153 635
c91f1830 636 return null;
f433d153
NR
637 }
638
4536c5cf
NR
639 private Image authorCover(String author, WLoginResult login)
640 throws IOException {
641 Image img = null;
642
643 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
644 author, null);
645 if (metas.size() > 0) {
646 BasicLibrary lib = Instance.getInstance().getLibrary();
647 img = lib.getCustomAuthorCover(author);
648 if (img == null)
649 img = lib.getCover(metas.get(0).getLuid());
650 }
651
652 return img;
653
654 }
655
656 private void authorCover(String author, WLoginResult login, String luid)
657 throws IOException {
658 if (meta(luid, login) != null) {
659 List<MetaData> metas = new MetaResultList(metas(login)).filter(null,
660 author, null);
661 if (metas.size() > 0) {
662 BasicLibrary lib = Instance.getInstance().getLibrary();
663 lib.setAuthorCover(author, luid);
664 }
665 }
666 }
667
668 private Image sourceCover(String source, WLoginResult login)
669 throws IOException {
670 Image img = null;
671
672 List<MetaData> metas = new MetaResultList(metas(login)).filter(source,
673 null, null);
674 if (metas.size() > 0) {
675 BasicLibrary lib = Instance.getInstance().getLibrary();
676 img = lib.getCustomSourceCover(source);
677 if (img == null)
678 img = lib.getCover(metas.get(0).getLuid());
679 }
680
681 return img;
682 }
683
684 private void sourceCover(String source, WLoginResult login, String luid)
685 throws IOException {
686 if (meta(luid, login) != null) {
687 List<MetaData> metas = new MetaResultList(metas(login))
688 .filter(source, null, null);
689 if (metas.size() > 0) {
690 BasicLibrary lib = Instance.getInstance().getLibrary();
691 lib.setSourceCover(source, luid);
692 }
693 }
694 }
695
c91f1830 696 private boolean isAllowed(MetaData meta, WLoginResult login) {
4536c5cf
NR
697 MetaResultList one = new MetaResultList(Arrays.asList(meta));
698 if (login.isWl() && !whitelist.isEmpty()) {
699 if (one.filter(whitelist, null, null).isEmpty()) {
700 return false;
701 }
f433d153 702 }
4536c5cf
NR
703 if (login.isBl() && !blacklist.isEmpty()) {
704 if (!one.filter(blacklist, null, null).isEmpty()) {
705 return false;
706 }
b459e462
NR
707 }
708
c91f1830 709 return true;
b459e462
NR
710 }
711
c91f1830
NR
712 private long sizeOf(Story story) {
713 long size = 0;
714 for (Chapter chap : story) {
715 for (Paragraph para : chap) {
716 if (para.getType() == ParagraphType.IMAGE) {
717 size += para.getContentImage().getSize();
718 } else {
719 size += para.getContent().length();
720 }
721 }
b459e462
NR
722 }
723
c91f1830 724 return size;
b459e462
NR
725 }
726
727 public static void main(String[] args) throws IOException {
728 Instance.init();
729 WebLibraryServer web = new WebLibraryServer(false);
730 web.run();
731 }
f433d153 732}