package plugins.CENO.Client; import java.io.IOException; import java.net.MalformedURLException; import java.util.concurrent.TimeUnit; import plugins.CENO.Common.URLtoUSKTools; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; import freenet.client.FetchException; import freenet.client.FetchResult; import freenet.client.async.ClientContext; import freenet.client.async.USKCallback; import freenet.keys.FreenetURI; import freenet.keys.USK; import freenet.support.Logger; public class USKUpdateFetcher { public static boolean subscribeToUSK(FreenetURI uskUri, USKCallback uskCb) { long suggestedEdition; if (uskUri.isSSK()) { suggestedEdition = 0L; } else if (uskUri.isUSK()) { suggestedEdition = (uskUri.getSuggestedEdition() < 0) ? 0L : uskUri.getSuggestedEdition(); } else { return false; } USK usk; try { usk = new USK(uskUri.getRoutingKey(), uskUri.getCryptoKey(), uskUri.getExtra(), uskUri.getDocName(), suggestedEdition); } catch (MalformedURLException e) { Logger.warning(USKUpdateFetcher.class, "Could not subscribe to updates of Malformed USK"); return false; } CENOClient.nodeInterface.subscribeToUSK(usk, uskCb); return true; } public static boolean subscribeFetchUSK(String url, long suggestedEdition) { if (url != null && !url.isEmpty()) { try { url = URLtoUSKTools.validateURL(url); } catch (MalformedURLException e) { Logger.warning(USKUpdateFetcher.class, "URL failed validation: " + url + " msg: " + e.getMessage()); return false; } } try { subscribeToUSK(URLtoUSKTools.getPortalFeedsUSK(CENOClient.getBridgeKey()).setSuggestedEdition(suggestedEdition), new USKUpdateFetcher.USKFetchCallback()); } catch (MalformedURLException e) { return false; } return true; } public static boolean subscribeToFeedUSK(String url, long suggestedEdition) { if (url != null && !url.isEmpty()) { try { url = URLtoUSKTools.validateURL(url); if (!URLtoUSKTools.isFeedURL(url)) { Logger.warning(USKUpdateFetcher.class, "Feeds list included not feed url"); return false; } subscribeToUSK(URLtoUSKTools.computeUSKfromURL(url, CENOClient.getBridgeKey()), new USKUpdateFetcher.USKVoidCallback()); } catch (MalformedURLException e) { Logger.warning(USKUpdateFetcher.class, "Feed URL failed validation: " + url + " msg: " + e.getMessage()); return false; } } return true; } public static boolean subscribeToBridgeFeeds() { FreenetURI bridgeUri; USK feedsUSK; try { bridgeUri = URLtoUSKTools.computeUSKfromURL(URLtoUSKTools.PORTAL_DOC_NAME, CENOClient.getBridgeKey()); feedsUSK = new USK(bridgeUri.getRoutingKey(), bridgeUri.getCryptoKey(), bridgeUri.getExtra(), bridgeUri.getDocName(), CENOClient.getFeedsLastVersion()); } catch (MalformedURLException e) { Logger.error(USKUpdateFetcher.class, "Could not calculate the USK of CENO Portal feeds json"); return false; } CENOClient.nodeInterface.subscribeToUSK(feedsUSK, new USKUpdateFetcher.USKFetchCallback(true)); return true; } private static class USKVoidCallback implements USKCallback { @Override public void onFoundEdition(long l, USK key, ClientContext context, boolean metadata, short codec, byte[] data, boolean newKnownGood, boolean newSlotToo) { } @Override public short getPollingPriorityNormal() { return 0; } @Override public short getPollingPriorityProgress() { return 0; } } private static class USKFetchCallback implements USKCallback { private boolean fetchSubList = false; public USKFetchCallback() { } public USKFetchCallback(boolean fetchSublist) { this.fetchSubList = fetchSublist; } @Override public void onFoundEdition(long l, USK key, ClientContext context, boolean metadata, short codec, byte[] data, boolean newKnownGood, boolean newSlotToo) { fetchNewEdition(key.getURI().setSuggestedEdition(l)); } @Override public short getPollingPriorityNormal() { return 0; } @Override public short getPollingPriorityProgress() { return 0; } private void fetchNewEdition(FreenetURI uri) { FetchResult fetchResult = null; if (!uri.hasMetaStrings()) { uri = uri.addMetaStrings(new String[]{"default.html"}); } try { fetchResult = CENOClient.nodeInterface.fetchURI(uri); } catch (FetchException e) { switch (e.getMode()) { case PERMANENT_REDIRECT : if (fetchSubList) { CENOClient.setFeedsLastVersion(e.newURI.getSuggestedEdition()); } fetchNewEdition(e.newURI); break; case ALL_DATA_NOT_FOUND : case DATA_NOT_FOUND : Logger.warning(USKUpdateFetcher.class, "Found new edition of USK but could not fetch data for USK: " + uri); break; case RECENTLY_FAILED : try { Thread.sleep(TimeUnit.MINUTES.toMillis(10)); } catch (InterruptedException e1) { // No big deal } finally { fetchNewEdition(uri); } return; default: Logger.warning(USKUpdateFetcher.class, "Exception while fetching new edition for USK: " + uri + ", " + e.getMessage()); break; } if (e.isDefinitelyFatal()) { Logger.error(USKUpdateFetcher.class, "Fatal error while fetching new edition for USK: " + uri + ", " + e.getMessage()); return; } } CENOClient.setFeedsLastVersion(uri.getSuggestedEdition()); if (fetchSubList && fetchResult != null) { subscribeToFeeds(fetchResult); } return; } private void subscribeToFeeds(FetchResult result) { JSONParser parser = new JSONParser(JSONParser.MODE_JSON_SIMPLE); JSONObject obj = null; try { obj = (JSONObject)parser.parse(result.asByteArray()); } catch (ParseException e) { Logger.error(USKFetchCallback.class, "Could not parse feeds.json"); return; } catch (IOException e) { Logger.error(USKFetchCallback.class, "IOException while parsing feeds.json"); return; } JSONArray feeds = (JSONArray) obj.get("feeds"); if (feeds == null) { Logger.warning(USKFetchCallback.class, "Retrieved feeds.json without any feeds"); return; } for (Object feed : feeds) { String url = ((JSONObject)feed).get("url").toString(); if (url != null && !url.isEmpty()) { subscribeToFeedUSK(url, 0); } } } } }