package freenet.pluginmanager; import static java.util.Collections.unmodifiableCollection; import java.net.MalformedURLException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import freenet.keys.FreenetURI; import freenet.node.updater.NodeUpdater; import freenet.node.updater.PluginJarUpdater; /** * Container for Freenet’s official plugins. * * FIXME: Connectivity essential plugins shouldn't have their minimum version increased! * @see https://bugs.freenetproject.org/view.php?id=6600 * * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a> */ public class OfficialPlugins { private final Map<String, OfficialPluginDescription> officialPlugins = new HashMap<String, OfficialPluginDescription>(); public OfficialPlugins() { try { addPlugin("Freemail") .inGroup("communication") .minimumVersion(15) .usesXml() .loadedFrom("CHK@6dfMgGf7YEfJhF0W~K0HUv0fnbuRwYH6iMqrLIbTI7k,huYBf8oBevwW6lRQnz-0jDP1dl5ej7FKeyVZ3CnH0Ec,AAMC--8/Freemail.jar") .deprecated(); addPlugin("Freemail_wot") .inGroup("communication") .minimumVersion(27) .loadedFrom("CHK@fR~OePzegkidZcKQN3aX-2EmS8RPo6Qj3hNTKf~BmJA,Mb7laJ5Mz0vvnEIhYr6yZbAytgdhQeXxJDA2LL0ziZU,AAMC--8/Freemail-v0.2.7.2.jar"); addPlugin("HelloWorld") .inGroup("example") .loadedFrom("CHK@ZdTXnWV-ikkt25-y8jmhlHjCY-nikDMQwcYlWHww5eg,Usq3uRHpHuIRmMRRlNQE7BNveO1NwNI7oNKdb7cowFM,AAIC--8/HelloWorld.jar") .advanced(); addPlugin("HelloFCP") .inGroup("example") .loadedFrom("CHK@0gtXJpw1QUJCmFOhoPRNqhsNbMtVw1CGVe46FUv7-e0,X8QqhtPkHoaFCUd89bgNaKxX1AV0WNBVf3sRgSF51-g,AAIC--8/HelloFCP.jar") .advanced(); addPlugin("JSTUN") .inGroup("connectivity") .essential() .minimumVersion(2) .loadedFrom("CHK@Zgib8xrGxcEuix7AVB4eajton1FpNHbIJeQZgEbHMNU,BQekU261VLSDUBQPOHSMKUF5qxY1v0zjXa33RyoEbYk,AAMC--8/JSTUN.jar"); addPlugin("KeyUtils") .inGroup("technical") .minimumVersion(5026) .loadedFrom("CHK@VGVmYp8qGseh2S7irYSDS7f6Goy0Z7rZcOc-iiQVPXg,Q4VeVf0fhdfejLqXrSYY5TK76f37D34Ly3Npv5X1GMA,AAMC--8/KeyUtils-v5026.jar") .advanced(); addPlugin("MDNSDiscovery") .inGroup("connectivity") .minimumVersion(2) .loadedFrom("CHK@wPyhY61bsDM3OW6arFlxYX8~mBKjo~XtOTIAbT0dk88,Vr3MTAzkW5J28SJs2dTxkj6D4GVNm3u8GFsxJgzTL1M,AAIC--8/MDNSDiscovery.jar"); addPlugin("SNMP") .inGroup("connectivity") .loadedFrom("CHK@EykJIv83UE291zONVzfXqyJYX5t66uCQJHkzQrB61MI,-npuolPZj1fcAWane2~qzRNEjKDERx52aQ5bC6NBQgw,AAIC--8/SNMP.jar") .advanced(); addPlugin("TestGallery") .inGroup("example") .minimumVersion(1) .loadedFrom("CHK@LfJVh1EkCr4ry0yDW74vwxkX-3nkr~ztW2z0SUZHfC0,-mz7l39dC6n0RTUiSokjC~pUDO7PWZ89miYesKH0-WA,AAIC--8/TestGallery.jar") .experimental(); addPlugin("ThawIndexBrowser") .inGroup("file-transfer") .minimumVersion(6) .usesXml() .loadedFrom("CHK@9bjNQtl7ndPKh~gi4woH0Xvb7uRunJ81deIlXwGE6qg,clwp0Bhx2LZxt2XCWeARqv24tBNmjlhXDZtwAJpzlIc,AAMC--8/ThawIndexBrowser-v6.jar"); addPlugin("UPnP") .inGroup("connectivity") .essential() .recommendedVersion(10007) .minimumVersion(10003) .loadedFrom( "CHK@ZiX8yeMHTUtNfJAgxpwH~jLRnnbb41BKEkAxOD~33tY,aBTvD3IoPKPLjnHOCNQ4-iRwqVED5kHgkmD4UhGdITk,AAMC--8/UPnP-10007.jar"); addPlugin("XMLLibrarian") .inGroup("index") .minimumVersion(26) .usesXml() .loadedFrom("CHK@TvjyCaG1dx0xIBSJkXSKA1ZT4I~NkRKeQqwC0a0bhFM,JiQe4CRjF1RwhQRFFQzP-ih9t2i0peV0tBCfJAeFCdk,AAIC--8/XMLLibrarian.jar") .unsupported(); addPlugin("XMLSpider") .inGroup("index") .minimumVersion(48) .usesXml() .loadedFrom("CHK@ne-aaLuzVZLcHj0YmrclaCXJqxsSb7q-J0eYEiL9V9o,v0EdgDGBhTE9k6GsB44UrQ4ADUq5LCUVknLaE4iSEBk,AAMC--8/XMLSpider.jar") .unsupported(); addPlugin("Freereader") .inGroup("index") .minimumVersion(6) .usesXml() .loadedFrom("CHK@SjXgPC5IEZa2g7a6gIKmNuxEKN4~eWrPhIwsznmGV-8,QUxm9R3sp3mNwhEHhL8mlx9zbOhIqIyR93tu7jD~0EU,AAMC--8/Freereader-6.jar"); addPlugin("Library") .inGroup("index") .recommendedVersion(37) .minimumVersion(36) .usesXml() .loadedFrom("CHK@qh6MyHn0umAm5luL3Ak1dJxj39vGJeiLp6KdtPTng08,PRwIA7m8bhMsbhMR8M6wWmd4iH~op3ImjsdCW1XBGmQ,AAMC--8/Library-v37.jar"); addPlugin("Spider") .inGroup("index") .minimumVersion(52) .loadedFrom("CHK@94gCPJEkEXq6Zti4wxDrqr9e~geQS4B3kdIwl4TXzV8,NUlmfjeqja28Lim6m3kTuxGHRSNtQHsbRoIAilxdkJY,AAMC--8/Spider-v52.jar") .advanced(); addPlugin("WebOfTrust") .inGroup("communication") .minimumVersion(18) .usesXml() .loadedFrom("CHK@qprIfrwqW0zg4izvRqStw0GUR2siJBe6LuDuy-t6D7A,eVd5fUG5nrNnIl7vwlRGzyhTe~TTZwuh6P1oqZH8g0A,AAMC--8/WebOfTrust-build0018.jar"); addPlugin("WebOfTrustTesting") .inGroup("communication") .advanced() .experimental() .usesXml() .alwaysFetchLatestVersion() .minimumVersion(17) // When changing this also update edition of USK below! .loadedFrom("USK@QeTBVWTwBldfI-lrF~xf0nqFVDdQoSUghT~PvhyJ1NE,OjEywGD063La2H-IihD7iYtZm3rC0BP6UTvvwyF5Zh4,AQACAAE/WebOfTrustTesting.jar/17"); addPlugin("FlogHelper") .inGroup("communication") .minimumVersion(35) .usesXml() .loadedFrom("CHK@3Ht6otYHogKxa1Z8SC0hz46kV9q1qaSBsjbJxuwPosU,txky3jF33oWU9n6YiGMphiHA1V9q3i4ZN0VPtgElSaI,AAMC--8/FlogHelper-v35.jar"); addPlugin("Sharesite") .inGroup("communication") .recommendedVersion(4) .minimumVersion(2) .loadedFrom("CHK@MktEdCJqSCG~5LaI9f~3xoQjejZCEVraQlfzKRs-H3I,yI2mQDrN1omHqD6ZibXDTRqnCK~2ut4Q11p18J1~cGE,AAMC--8/Sharesite-0.4.4.jar"); } catch (MalformedURLException mue1) { throw new RuntimeException("Could not create FreenetURI.", mue1); } } private OfficialPluginBuilder addPlugin(String name) { return new OfficialPluginBuilder(name); } public OfficialPluginDescription get(String name) { return officialPlugins.get(name); } public Collection<OfficialPluginDescription> getAll() { return unmodifiableCollection(officialPlugins.values()); } private class OfficialPluginBuilder { private final String name; private String group; private boolean essential; private long minimumVersion = -1; private long recommendedVersion = -1; /** @see OfficialPluginDescription#alwaysFetchLatestVersion */ private boolean alwaysFetchLatestVersion; private boolean usesXml; /** @see OfficialPluginDescription#uri */ private FreenetURI uri; private boolean deprecated; private boolean experimental; private boolean advanced; private boolean unsupported; private OfficialPluginBuilder(String name) { this.name = name; addCurrentPluginDescription(); } public OfficialPluginBuilder inGroup(String group) { this.group = group; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder essential() { essential = true; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder minimumVersion(int minimumVersion) { this.minimumVersion = minimumVersion; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder recommendedVersion(int recommendedVersion) { this.recommendedVersion = recommendedVersion; addCurrentPluginDescription(); return this; } /** @see OfficialPluginDescription#alwaysFetchLatestVersion */ public OfficialPluginBuilder alwaysFetchLatestVersion() { this.alwaysFetchLatestVersion = true; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder usesXml() { usesXml = true; addCurrentPluginDescription(); return this; } /** * ATTENTION: Please read {@link OfficialPluginDescription#uri} before deciding whether * to use USK or CHK! */ public OfficialPluginBuilder loadedFrom(String uri) throws MalformedURLException { this.uri = new FreenetURI(uri); addCurrentPluginDescription(); return this; } public OfficialPluginBuilder deprecated() { deprecated = true; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder experimental() { experimental = true; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder advanced() { advanced = true; addCurrentPluginDescription(); return this; } public OfficialPluginBuilder unsupported() { unsupported = true; addCurrentPluginDescription(); return this; } private void addCurrentPluginDescription() { if(recommendedVersion == 0 && minimumVersion > 0) recommendedVersion = minimumVersion; if(minimumVersion == 0 && recommendedVersion > 0) minimumVersion = recommendedVersion; officialPlugins.put(name, createOfficialPluginDescription()); } private OfficialPluginDescription createOfficialPluginDescription() { return new OfficialPluginDescription(name, group, essential, minimumVersion, recommendedVersion, alwaysFetchLatestVersion, usesXml, uri, deprecated, experimental, advanced, unsupported); } } public static class OfficialPluginDescription { /** The name of the plugin */ public final String name; /** * The group of the plugin. The group is a technical name that needs to * be translated before it is shown to the user. */ public final String group; /** * If true, we will download it, blocking, over HTTP, during startup (unless * explicitly forbidden to use HTTP). If not, we will download it on a * separate thread after startup. Both are assuming we don't have it in a * file. */ public final boolean essential; /** * Minimum getRealVersion(). If the plugin is older than this, we will fail * the load. */ public final long minimumVersion; /** * Recommended getRealVersion(). If the plugin is older than this, we will * download the new version in the background, and either use it on restart, * or offer the user the option to reload it. This is in fact identical to * what happens on a USK-based update... */ public final long recommendedVersion; /** * If true, if during startup we already have a copy of the plugin JAR on disk, the * {@link PluginManager} will ignore it and redownload the JAR instead so the user gets a * recent version if there is one.<br><br> * * This is for being used together with plugins which are fetched from a USK {@link #uri}, * and which are not included in the official main Freenet update USK which * {@link PluginJarUpdater} watches.<br> * For plugins which are in the main Freenet update USK, setting this to true is usually * not necessary: The {@link PluginJarUpdater} will update the plugin if there is a new * version.<br><br> * * In other words: Plugins which are NOT in the official USK but have their own USK will * not have the {@link PluginJarUpdater} monitor their USK, it only monitors the main * USK. Thus, the only chance to update them is during startup by ignoring the JAR and * causing a re-download of it. */ public final boolean alwaysFetchLatestVersion; /** Does it use XML? If so, if the JVM is vulnerable, then don't load it */ public final boolean usesXML; /** * FreenetURI to get the latest version from.<br> * Typically a CHK, not USK, since updates are deployed using the main Freenet USK of * {@link NodeUpdater}'s subclass {@link PluginJarUpdater}.<br><br> * * To allow people to insert plugin updates without giving them write access to the main * USK, this *can* be an USK, but updating when a new version is inserted to the USK will * only happen at certain points in time:<br> * - if the plugin is manually unloaded and loaded again.<br> * - at restart of Freenet if {@link #alwaysFetchLatestVersion} is true. If it is false, the * cached local JAR file on disk will prevent updating!<br> * So to make updating work using USK, set {@link #alwaysFetchLatestVersion} so we check * for updates when the node is restarted.<br><br> * * NOTICE the conclusion of the above: It is NOT RECOMMENDED to use USKs here: Updates will * only be delivered at restarts of the node, while the main Freenet USK supports live * updates; and also there is no revocation mechanism for the USKs. Instead of using USKs * here, a CHK should be preferred, and new plugin versions then should be inserted at the * main Freenet update USK of the the {@link NodeUpdater}. A typical usecase for * nevertheless using an USK here is to allow individual plugin developers to push testing * versions of their plugin on their own without giving them write-access to the main * Freenet update USK.*/ public final FreenetURI uri; /** If true, the plugin is obsolete. */ public final boolean deprecated; /** If true, the plugin is experimental. */ public final boolean experimental; /** * If true, the plugin is geeky - it should not be shown except in advanced * mode even though it's not deprecated nor is it experimental. */ public final boolean advanced; /** * If true, the plugin used to be official, but is no longer supported. * These are not shown even in advanced mode. */ public final boolean unsupported; OfficialPluginDescription(String name, String group, boolean essential, long minVer, long recVer, boolean alwaysFetchLatestVersion, boolean usesXML, FreenetURI uri, boolean deprecated, boolean experimental, boolean advanced, boolean unsupported) { this.name = name; this.group = group; this.essential = essential; this.minimumVersion = minVer; this.recommendedVersion = recVer; this.alwaysFetchLatestVersion = alwaysFetchLatestVersion; this.usesXML = usesXML; this.deprecated = deprecated; this.experimental = experimental; this.advanced = advanced; this.unsupported = unsupported; if (alwaysFetchLatestVersion && uri != null) { assert(uri.isUSK()) : "Non-USK URIs do not support updates!"; // Force fetching the latest edition by setting a negative USK edition. long edition = uri.getSuggestedEdition(); if (edition >= 0) { edition = Math.min(-1, -edition); } uri = uri.setSuggestedEdition(edition); } this.uri = uri; } public String getLocalisedPluginName() { return PluginManager.getOfficialPluginLocalisedName(name); } public String getLocalisedPluginDescription() { return PluginManager.l10n("pluginDesc." + name); } } }