package net.i2p.router.web; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Reader; import java.text.SimpleDateFormat; import java.util.Date; import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import net.i2p.router.update.ConsoleUpdateManager; import static net.i2p.update.UpdateType.*; import net.i2p.util.TranslateReader; /** * If news file does not exist, use file from the initialNews directory * in $I2P * * @since 0.8.2 */ public class NewsHelper extends ContentHelper { public static final String PROP_LAST_UPDATE_TIME = "router.updateLastDownloaded"; /** @since 0.8.12 */ private static final String PROP_LAST_HIDDEN = "routerconsole.newsLastHidden"; /** @since 0.9.4 */ public static final String PROP_LAST_CHECKED = "routerconsole.newsLastChecked"; /** @since 0.9.4 */ public static final String PROP_LAST_UPDATED = "routerconsole.newsLastUpdated"; /** * Default true * @since 0.9.21 */ public static final String PROP_TRANSLATE = "routerconsole.newsTranslate"; public static final String NEWS_FILE = "docs/news.xml"; /** * If ANY update is in progress. * @since 0.9.4 was stored in system properties */ public static boolean isAnyUpdateInProgress() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return false; return mgr.isUpdateInProgress(); } /** * If a signed or unsigned router update is in progress. * Does NOT cover plugins, news, etc. * @since 0.9.4 was stored in system properties */ public static boolean isUpdateInProgress() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return false; return mgr.isUpdateInProgress(ROUTER_SIGNED) || mgr.isUpdateInProgress(ROUTER_SIGNED_SU3) || mgr.isUpdateInProgress(ROUTER_UNSIGNED) || mgr.isUpdateInProgress(ROUTER_DEV_SU3) || mgr.isUpdateInProgress(TYPE_DUMMY); } /** * Release update only. * Will be false if already downloaded. * @since 0.9.4 moved from NewsFetcher */ public static boolean isUpdateAvailable() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return false; return mgr.getUpdateAvailable(ROUTER_SIGNED) != null || mgr.getUpdateAvailable(ROUTER_SIGNED_SU3) != null; } /** * Release update only. * Available version, will be null if already downloaded. * @return null if none * @since 0.9.4 moved from NewsFetcher */ public static String updateVersion() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; String rv = mgr.getUpdateAvailable(ROUTER_SIGNED_SU3); if (rv != null) return rv; return mgr.getUpdateAvailable(ROUTER_SIGNED); } /** * Release update only. * Translated message about new version available but constrained * @return null if none * @since 0.9.9 */ public static String updateConstraint() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return mgr.getUpdateConstraint(ROUTER_SIGNED, ""); } /** * Translated message about new version available but constrained * @return null if none * @since 0.9.23 */ public static String unsignedUpdateConstraint() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return mgr.getUpdateConstraint(ROUTER_UNSIGNED, ""); } /** * Translated message about new version available but constrained * @return null if none * @since 0.9.23 */ public static String devSU3UpdateConstraint() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return mgr.getUpdateConstraint(ROUTER_DEV_SU3, ""); } /** * Release update only. * Already downloaded but not installed version. * @return null if none * @since 0.9.4 */ public static String updateVersionDownloaded() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; String rv = mgr.getUpdateDownloaded(ROUTER_SIGNED_SU3); if (rv != null) return rv; return mgr.getUpdateDownloaded(ROUTER_SIGNED); } /** * Will be false if already downloaded or if dev update disabled. * @since 0.9.4 moved from NewsFetcher */ public static boolean isUnsignedUpdateAvailable(RouterContext ctx) { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return false; return mgr.getUpdateAvailable(ROUTER_UNSIGNED) != null && ctx.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED); } /** * @return null if none * @since 0.9.4 moved from NewsFetcher */ public static String unsignedUpdateVersion() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return formatUnsignedVersion(mgr.getUpdateAvailable(ROUTER_UNSIGNED)); } /** * Already downloaded but not installed version * @return null if none * @since 0.9.4 */ public static String unsignedVersionDownloaded() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return formatUnsignedVersion(mgr.getUpdateDownloaded(ROUTER_UNSIGNED)); } /** * Will be false if already downloaded or if dev update disabled. * @since 0.9.20 */ public static boolean isDevSU3UpdateAvailable(RouterContext ctx) { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return false; return mgr.getUpdateAvailable(ROUTER_DEV_SU3) != null && ctx.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_DEV_SU3); } /** * @return null if none * @since 0.9.20 */ public static String devSU3UpdateVersion() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return mgr.getUpdateAvailable(ROUTER_DEV_SU3); } /** * Already downloaded but not installed version * @return null if none * @since 0.9.20 */ public static String devSU3VersionDownloaded() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return null; return mgr.getUpdateDownloaded(ROUTER_DEV_SU3); } /** * Convert long date stamp to * '07-Jul 21:09 UTC' with month name in the system locale * @return null if ver = null * @since 0.9.4 moved from NewsFetcher */ private static String formatUnsignedVersion(String ver) { if (ver != null) { try { long modtime = Long.parseLong(ver); return (new SimpleDateFormat("dd-MMM HH:mm")).format(new Date(modtime)) + " UTC"; } catch (NumberFormatException nfe) {} } return null; } /** * @return "" if none * @since 0.9.4 moved from UpdateHelper */ public static String getUpdateStatus() { ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); if (mgr == null) return ""; return mgr.getStatus(); } /** * If we haven't downloaded news yet, use the translated initial news file */ @Override public String getContent() { // show a min of 1, max of 3, none older than 60 days over min return NewsFeedHelper.getEntries(_context, 0, 3, 60*24*60*60*1000L); } /** * Is the news newer than the last time it was hidden? * @since 0.8.12 */ public boolean shouldShowNews() { return shouldShowNews(_context); } /** * @since 0.9.4 */ public static boolean shouldShowNews(RouterContext ctx) { long lastUpdated = lastUpdated(ctx); if (lastUpdated <= 0) return true; long last = ctx.getProperty(PROP_LAST_HIDDEN, 0L); return lastUpdated > last; } /** * Save config with the timestamp of the current news to hide, or 0 to show * @since 0.8.12 */ public void showNews(boolean yes) { showNews(_context, yes); } /** * Save config with the timestamp of the current news to hide, or 0 to show * @since 0.9.4 */ public static void showNews(RouterContext ctx, boolean yes) { long stamp = yes ? 0 : lastUpdated(ctx); ctx.router().saveConfig(PROP_LAST_HIDDEN, Long.toString(stamp)); } /** * @return HTML * @since 0.9.4 moved from NewsFetcher */ public String status() { return status(_context); } /** * @return HTML * @since 0.9.4 moved from NewsFetcher */ public static String status(RouterContext ctx) { StringBuilder buf = new StringBuilder(128); long now = ctx.clock().now(); buf.append("<i>"); long lastUpdated = lastUpdated(ctx); long lastFetch = lastChecked(ctx); if (lastUpdated > 0) { buf.append(Messages.getString("News last updated {0} ago.", DataHelper.formatDuration2(now - lastUpdated), ctx)) .append('\n'); } if (lastFetch > lastUpdated) { buf.append(Messages.getString("News last checked {0} ago.", DataHelper.formatDuration2(now - lastFetch), ctx)); } buf.append("</i>"); String consoleNonce = CSSHelper.getNonce(); if (lastUpdated > 0 && consoleNonce != null) { if (shouldShowNews(ctx)) { buf.append(" <a href=\"/?news=0&consoleNonce=").append(consoleNonce).append("\">") .append(Messages.getString("Hide news", ctx)); } else { buf.append(" <a href=\"/?news=1&consoleNonce=").append(consoleNonce).append("\">") .append(Messages.getString("Show news", ctx)); } buf.append("</a>" + " - <a href=\"/news\">") .append(Messages.getString("Show all news", ctx)) .append("</a>"); } return buf.toString(); } /** * @since 0.9.4 moved from NewsFetcher */ public static boolean dontInstall(RouterContext ctx) { return isUpdateDisabled(ctx) || isBaseReadonly(ctx); } /** * @since 0.9.9 */ public static boolean isUpdateDisabled(RouterContext ctx) { return ctx.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_DISABLED); } /** * @since 0.9.9 */ public static boolean isBaseReadonly(RouterContext ctx) { File test = new File(ctx.getBaseDir(), "history.txt"); boolean readonly = ((test.exists() && !test.canWrite()) || (!ctx.getBaseDir().canWrite())); return readonly; } /** * @since 0.9.4 */ public static long lastChecked(RouterContext ctx) { return ctx.getProperty(PROP_LAST_CHECKED, 0L); } /** * When the news was last downloaded * @since 0.9.4 */ public static long lastUpdated(RouterContext ctx) { long rv = ctx.getProperty(PROP_LAST_UPDATED, 0L); if (rv > 0) return rv; File newsFile = new File(ctx.getRouterDir(), NEWS_FILE); rv = newsFile.lastModified(); ctx.router().saveConfig(PROP_LAST_UPDATED, Long.toString(rv)); return rv; } }