I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
  • jrandom's avatar
    33366cc2
    2005-04-01 jrandom · 33366cc2
    jrandom authored and zzz's avatar zzz committed
        * Fix to check for missing news file (thanks smeghead!)
        * Added destination display CLI:
          java -cp lib/i2p.jar net.i2p.data.Destination privKeyFilename
        * Added destination display to the web interface (thanks pnspns)
        * Installed CIA backdoor
    33366cc2
    History
    2005-04-01 jrandom
    jrandom authored and zzz's avatar zzz committed
        * Fix to check for missing news file (thanks smeghead!)
        * Added destination display CLI:
          java -cp lib/i2p.jar net.i2p.data.Destination privKeyFilename
        * Added destination display to the web interface (thanks pnspns)
        * Installed CIA backdoor
To find the state of this project's repository at the time of any of these versions, check out the tags.
NewsFetcher.java 9.88 KiB
package net.i2p.router.web;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.StringTokenizer;

import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;

/**
 * Task to periodically look for updates to the news.xml, and to keep
 * track of whether that has an announcement for a new version.
 */
public class NewsFetcher implements Runnable, EepGet.StatusListener {
    private I2PAppContext _context;
    private Log _log;
    private boolean _updateAvailable;
    private long _lastFetch;
    private static NewsFetcher _instance;
    public static final NewsFetcher getInstance() { return _instance; }
    
    private static final String NEWS_FILE = "docs/news.xml";
    private static final String TEMP_NEWS_FILE = "docs/news.xml.temp";
    
    public NewsFetcher(I2PAppContext ctx) {
        _context = ctx;
        _log = ctx.logManager().getLog(NewsFetcher.class);
        _instance = this;
        updateLastFetched();
    }
    
    private void updateLastFetched() {
        File news = new File(NEWS_FILE);
        if (news.exists())
            _lastFetch = news.lastModified();
        else
            _lastFetch = 0;
    }
    
    public boolean updateAvailable() { return _updateAvailable; }
    
    public void run() {
        try { Thread.sleep(_context.random().nextLong(5*60*1000)); } catch (InterruptedException ie) {}
        while (true) {
            if (!_updateAvailable) checkForUpdates();
            if (shouldFetchNews())
                fetchNews();
            try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
        }
    }
    
    private boolean shouldInstall() {
        String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
        return ("install".equals(policy));
    }
    
    private boolean shouldFetchNews() {
        updateLastFetched();
        String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY);
        if (freq == null)
            freq = ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY;
        try {
            long ms = Long.parseLong(freq);
            if (ms <= 0)
                return false;
            
            if (_lastFetch + ms < _context.clock().now()) {
                return true;
            } else {
                if (_log.shouldLog(Log.DEBUG))
                    _log.debug("Last fetched " + DataHelper.formatDuration(_context.clock().now() - _lastFetch) + " ago");
                return false;
            }
        } catch (NumberFormatException nfe) {
            if (_log.shouldLog(Log.ERROR))
                _log.error("Invalid refresh frequency: " + freq);
            return false;
        }
    }
    private void fetchNews() {
        String newsURL = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL, ConfigUpdateHandler.DEFAULT_NEWS_URL);
        boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
        String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
        String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT);
        File tempFile = new File(TEMP_NEWS_FILE);
        if (tempFile.exists())
            tempFile.delete();
        
        int proxyPort = -1;
        try {
            proxyPort = Integer.parseInt(port);
            EepGet get = null;
            if (shouldProxy)
                get = new EepGet(_context, proxyHost, proxyPort, 10, TEMP_NEWS_FILE, newsURL);
            else
                get = new EepGet(_context, 10, TEMP_NEWS_FILE, newsURL);
            get.addStatusListener(this);
            get.fetch();
        } catch (Throwable t) {
            _log.error("Error fetching the news", t);
        }

        _lastFetch = _context.clock().now();
    }
    
    private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
    private static final String VERSION_PREFIX = "version=\"";
    private void checkForUpdates() {
        File news = new File(NEWS_FILE);
        if (!news.exists()) return;
        FileInputStream in = null;
        try {
            in = new FileInputStream(news);
            StringBuffer buf = new StringBuffer(128);
            while (DataHelper.readLine(in, buf)) {
                int index = buf.indexOf(VERSION_PREFIX);
                if (index == -1) {
                    // skip
                } else {
                    int end = buf.indexOf("\"", index + VERSION_PREFIX.length());
                    if (end > index) {
                        String ver = buf.substring(index+VERSION_PREFIX.length(), end);
                        if (_log.shouldLog(Log.DEBUG))
                            _log.debug("Found version: [" + ver + "]");
                        if (needsUpdate(ver)) {
                            if (_log.shouldLog(Log.DEBUG))
                                _log.debug("Our version is out of date, update!");
                            break;
                        } else {
                            if (_log.shouldLog(Log.DEBUG))
                                _log.debug("Our version is current");
                            return;
                        }
                    }
                }
                if (buf.indexOf(VERSION_STRING) != -1) {
                    if (_log.shouldLog(Log.DEBUG))
                        _log.debug("Our version found, no need to update: " + buf.toString());
                    return;
                } else {
                    if (_log.shouldLog(Log.DEBUG))
                        _log.debug("No match in " + buf.toString());
                }
                buf.setLength(0);
            }
        } catch (IOException ioe) {
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error checking the news for an update", ioe);
            return;
        } finally {
            if (in != null) try { in.close(); } catch (IOException ioe) {}
        }
        // could not find version="0.5.0.1", so there must be an update ;)
        
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("Our version was NOT found (" + RouterVersion.VERSION + "), update needed");
        _updateAvailable = true;
        
        if (shouldInstall()) {
            if (_log.shouldLog(Log.DEBUG))
                _log.debug("Policy requests update, so we update");
            UpdateHandler handler = null;
            if (_context instanceof RouterContext) {
                handler = new UpdateHandler((RouterContext)_context);
            } else {
                List contexts = RouterContext.listContexts();
                if (contexts.size() > 0)
                    handler = new UpdateHandler((RouterContext)contexts.get(0));
                else
                    _log.log(Log.CRIT, "No router context to update with?");
            }
            if (handler != null)
                handler.update();
        } else {
            if (_log.shouldLog(Log.DEBUG))
                _log.debug("Policy requests manual update, so we do nothing");
        }
    }
    
    private boolean needsUpdate(String version) {
        StringTokenizer newTok = new StringTokenizer(sanitize(version), ".");
        StringTokenizer ourTok = new StringTokenizer(sanitize(RouterVersion.VERSION), ".");
        
        while (newTok.hasMoreTokens() && ourTok.hasMoreTokens()) {
            String newVer = newTok.nextToken();
            String oldVer = ourTok.nextToken();
            switch (compare(newVer, oldVer)) {
                case -1: // newVer is smaller
                    return false;
                case 0: // eq
                    break;
                case 1: // newVer is larger
                    return true;
            }
        }
        if (newTok.hasMoreTokens() && !ourTok.hasMoreTokens())
            return true;
        return false;
    }
    
    private static final String VALID = "0123456789.";
    private static final String sanitize(String str) {
        StringBuffer buf = new StringBuffer(str);
        for (int i = 0; i < buf.length(); i++) {
            if (VALID.indexOf(buf.charAt(i)) == -1) {
                buf.deleteCharAt(i);
                i--;
            }
        }
        return buf.toString();
    }
    
    private static final int compare(String lhs, String rhs) {
        try {
            int left = Integer.parseInt(lhs);
            int right = Integer.parseInt(rhs);
            if (left < right) 
                return -1;
            else if (left == right)
                return 0;
            else
                return 1;
        } catch (NumberFormatException nfe) {
            return 0;
        }
    }
    
    public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
        // ignore
    }
    public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
        // ignore
    }
    public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
        if (_log.shouldLog(Log.INFO))
            _log.info("News fetched from " + url);
        
        File temp = new File(TEMP_NEWS_FILE);
        if (temp.exists()) {
            boolean copied = FileUtil.copy(TEMP_NEWS_FILE, NEWS_FILE, true);
            if (copied)
                temp.delete();
        }
        checkForUpdates();
    }
    
    public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
        if (_log.shouldLog(Log.ERROR))
            _log.error("Failed to fetch the news from " + url);
        File temp = new File(TEMP_NEWS_FILE);
        temp.delete();
    }
}