/* * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. If * not, see . */ package com.l2dc.gameserver.cache; import java.io.BufferedInputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.util.Arrays; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.l2dc.Config; import com.l2dcproject.commons.util.ResourceUtil; import com.l2dc.gameserver.model.actor.instance.L2PcInstance; import com.l2dc.gameserver.templates.htm.HtmTemplateEngine; import com.l2dc.gameserver.util.Util; /** * @author Layane * */ public class HtmCache { private static final Logger LOG = LoggerFactory.getLogger(HtmCache.class); private final ConcurrentHashMap _cache = new ConcurrentHashMap(); private static final String[] VALID_TAGS = { "column", "unknown", "ul", "u", "tt", "tr", "title", "textcode", "textarea", "td", "table", "sup", "sub", "strike", "spin", "select", "right", "pre", "p", "option", "ol", "multiedit", "li", "left", "input", "img", "i", "html", "h7", "h6", "h5", "h4", "h3", "h2", "h1", "font", "extend", "edit", "comment", "combobox", "center", "button", "br", "br1", "body", "bar", "address", "a", "sel", "list", "var", "fore", "readonl", "rows", "valign", "fixwidth", "bordercolorli", "bordercolorda", "bordercolor", "border", "bgcolor", "background", "align", "valu", "readonly", "multiple", "selected", "typ", "type", "maxlength", "checked", "src", "y", "x", "querydelay", "noscrollbar", "imgsrc", "b", "fg", "size", "face", "color", "deffon", "deffixedfont", "width", "value", "tooltip", "name", "min", "max", "height", "disabled", "align", "msg", "link", "href", "action", "head", "tbody", "!--" }; private final static HashSet VALID_TAGS_SET = new HashSet(301); static { VALID_TAGS_SET.addAll(Arrays.asList(VALID_TAGS)); } private int _loadedFiles; private long _bytesBuffLen; /** * * @return */ public static HtmCache getInstance() { return SingletonHolder.INSTANCE; } private HtmCache() { init(Config.DATAPACK_ROOT); } /** * */ public void reload() { HtmTemplateEngine.getInstance().reload(); init(Config.DATAPACK_ROOT); } /** * * @param f */ public void init(File f) { if (!Config.LAZY_CACHE) { LOG.info("Html cache start..."); parseDir(f); LOG.info("Cache[HTML]: {} megabytes on {} files loaded", String.format("%.3f", getMemoryUsage()), getLoadedFiles()); } else { _cache.clear(); _loadedFiles = 0; _bytesBuffLen = 0; LOG.info("Cache[HTML]: Running lazy cache"); } } /** * * @param f */ public void reloadPath(File f) { parseDir(f); LOG.info("Cache[HTML]: Reloaded specified path"); } /** * * @return */ public double getMemoryUsage() { return ((float) _bytesBuffLen / 1048576); } /** * * @return */ public int getLoadedFiles() { return _loadedFiles; } private static class HtmFilter implements FileFilter { public HtmFilter() { } @Override public boolean accept(File file) { if (!file.isDirectory()) { return (file.getName().endsWith(".htm") || file.getName().endsWith(".html")); } return true; } } private void parseDir(File dir) { final FileFilter filter = new HtmFilter(); final File[] files = dir.listFiles(filter); for (File file : files) { if (!file.isDirectory()) loadFile(file); else parseDir(file); } } /** * * @param file * @return */ public String loadFile(File file) { final String relpath = Util.getRelativePath(Config.DATAPACK_ROOT, file); final HtmFilter filter = new HtmFilter(); if (file.exists() && filter.accept(file) && !file.isDirectory()) { String content; FileInputStream fis = null; try { fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); final int bytes = bis.available(); final byte[] raw = new byte[bytes]; bis.read(raw); bis.close(); content = new String(raw, "UTF-8"); content = content.replaceAll("\r\n", "\n"); content = HtmTemplateEngine.getInstance().prepare(content, file.getAbsolutePath()); String oldContent = _cache.get(relpath); if (oldContent == null) { _bytesBuffLen += bytes; _loadedFiles++; } else { _bytesBuffLen = _bytesBuffLen - oldContent.length() + bytes; } if(!Config.LAZY_CACHE) validate(file.getPath(), content); _cache.put(relpath, content); return content; } catch (Exception e) { LOG.error("Cache[HTML]: Problem with htm file {}", file.getAbsolutePath(), e); } finally { ResourceUtil.closeInputStream(fis); } } return null; } /** * * @param path * @param player * @return */ public String getHtmForce(String path, L2PcInstance player) { String content = getHtm(path, player); if (content == null) { content = "My text is missing:
" + path + ""; LOG.warn("Cache[HTML]: Missing HTML page: {}", path); } return content; } /** * * @param path * @param player * @return */ public String getHtm(String path, L2PcInstance player) { return getHtm(path, player, null); } /** * * @param path * @param player * @param htmlPrefix - Temp parameter for community board caching, should be removed asap * @return */ public String getHtm(String path, L2PcInstance player, String htmlPrefix) { String newPath = null; String content; if (player != null) { newPath = player.getHtmlPrefix() + path; content = getHtm(newPath); if (content != null) return HtmTemplateEngine.getInstance().parse(content, player, path); } else if(htmlPrefix != null && !htmlPrefix.isEmpty()) { newPath = htmlPrefix + path; content = getHtm(newPath); if (content != null) return content; } content = getHtm(path); if (content != null && newPath != null) _cache.put(newPath, content); return HtmTemplateEngine.getInstance().parse(content, player, path); } private String getHtm(String path) { if (path == null || path.isEmpty()) return ""; // avoid possible NPE String content = _cache.get(path); if (Config.LAZY_CACHE && content == null) content = loadFile(new File(Config.DATAPACK_ROOT, path)); return content; } /** * * @param path * @return */ public boolean contains(String path) { return _cache.containsKey(path); } /** * Check if an HTM exists and can be loaded * * @param path * The path to the HTM * @return * */ public boolean isLoadable(String path) { File file = new File(Config.DATAPACK_ROOT, path); HtmFilter filter = new HtmFilter(); if (file.exists() && filter.accept(file) && !file.isDirectory()) return true; return false; } private void validate(String filename, String html) { outer: for (int begin = 0; (begin = html.indexOf('<', begin)) != -1; begin++) { int end; for (end = begin; end < html.length(); end++) { if (html.charAt(end) == '>' || html.charAt(end) == ' ') break; // some special quest-replaced tag if (end == begin + 1 && html.charAt(end) == '?') continue outer; } end++; String tag = html.substring(begin + 1, end - 1).toLowerCase().replaceAll("/", ""); if (!VALID_TAGS_SET.contains(tag)) { LOG.info("Cache[HTML]: Invalid tag used: '{}' at pos {} {}", new Object[] {tag, (begin + 1), filename}); } } } @SuppressWarnings("synthetic-access") private static class SingletonHolder { protected static final HtmCache INSTANCE = new HtmCache(); } }