1515import java .util .Optional ;
1616import java .util .function .Supplier ;
1717import java .util .stream .Collectors ;
18- import java .util .stream .Stream ;
1918
2019import org .jetbrains .annotations .Nullable ;
2120import org .slf4j .Logger ;
2221import org .slf4j .LoggerFactory ;
2322
2423import jadx .api .plugins .JadxPlugin ;
2524import jadx .api .plugins .JadxPluginInfo ;
25+ import jadx .api .plugins .utils .CommonFileUtils ;
2626import jadx .core .Jadx ;
2727import jadx .core .plugins .versions .VerifyRequiredVersion ;
2828import jadx .core .utils .StringUtils ;
2929import jadx .core .utils .Utils ;
3030import jadx .core .utils .exceptions .JadxRuntimeException ;
31+ import jadx .core .utils .files .FileUtils ;
3132import jadx .plugins .tools .data .JadxInstalledPlugins ;
3233import jadx .plugins .tools .data .JadxPluginMetadata ;
3334import jadx .plugins .tools .data .JadxPluginUpdate ;
3435import jadx .plugins .tools .resolvers .IJadxPluginResolver ;
3536import jadx .plugins .tools .resolvers .ResolversRegistry ;
3637import jadx .plugins .tools .utils .PluginUtils ;
38+ import jadx .zip .IZipEntry ;
39+ import jadx .zip .ZipContent ;
40+ import jadx .zip .ZipReader ;
3741
3842import static jadx .core .utils .GsonUtils .buildGson ;
3943import static jadx .plugins .tools .utils .PluginFiles .DROPINS_DIR ;
@@ -165,7 +169,7 @@ public boolean uninstall(String pluginId) {
165169 return false ;
166170 }
167171 JadxPluginMetadata plugin = found .get ();
168- deletePluginJar (plugin );
172+ deletePlugin (plugin );
169173 plugins .getInstalled ().remove (plugin );
170174 savePluginsJson (plugins );
171175 return true ;
@@ -189,24 +193,15 @@ public List<JadxPluginInfo> getAllPluginsInfo() {
189193 }
190194 }
191195
192- public List <Path > getAllPluginJars () {
193- List <Path > list = new ArrayList <>();
194- for (JadxPluginMetadata pluginMetadata : loadPluginsJson ().getInstalled ()) {
195- list .add (INSTALLED_DIR .resolve (pluginMetadata .getJar ()));
196- }
197- collectJarsFromDir (list , DROPINS_DIR );
198- return list ;
199- }
200-
201- public List <Path > getEnabledPluginJars () {
196+ public List <Path > getEnabledPluginPaths () {
202197 List <Path > list = new ArrayList <>();
203198 for (JadxPluginMetadata pluginMetadata : loadPluginsJson ().getInstalled ()) {
204199 if (pluginMetadata .isDisabled ()) {
205200 continue ;
206201 }
207- list .add (INSTALLED_DIR .resolve (pluginMetadata .getJar ()));
202+ list .add (INSTALLED_DIR .resolve (pluginMetadata .getPath ()));
208203 }
209- collectJarsFromDir ( list , DROPINS_DIR );
204+ list . addAll ( FileUtils . listFiles ( DROPINS_DIR ) );
210205 return list ;
211206 }
212207
@@ -254,46 +249,62 @@ private void install(JadxPluginMetadata metadata) {
254249 throw new JadxRuntimeException ("Can't install plugin, required version: \" " + reqVersionStr + '\"'
255250 + " is not compatible with current jadx version: " + Jadx .getVersion ());
256251 }
252+ // remove previous version
253+ uninstall (metadata .getPluginId ());
257254
258255 String version = metadata .getVersion ();
259- String fileName = metadata .getPluginId () + (StringUtils .notBlank (version ) ? '-' + version : "" ) + ".jar" ;
260- Path pluginJar = INSTALLED_DIR .resolve (fileName );
261- copyJar (Paths .get (metadata .getJar ()), pluginJar );
262- metadata .setJar (INSTALLED_DIR .relativize (pluginJar ).toString ());
263-
264- JadxInstalledPlugins plugins = loadPluginsJson ();
265- // remove previous version jar
266- plugins .getInstalled ().removeIf (p -> {
267- if (p .getPluginId ().equals (metadata .getPluginId ())) {
268- deletePluginJar (p );
269- return true ;
256+ String pluginBaseName = metadata .getPluginId () + (StringUtils .notBlank (version ) ? '-' + version : "" );
257+ String pluginPathStr = metadata .getPath ();
258+ Path pluginPath = Paths .get (pluginPathStr );
259+ if (pluginPathStr .endsWith (".jar" )) {
260+ Path pluginJar = INSTALLED_DIR .resolve (pluginBaseName + ".jar" );
261+ copyJar (pluginPath , pluginJar );
262+ metadata .setPath (INSTALLED_DIR .relativize (pluginJar ).toString ());
263+ } else if (Files .isDirectory (pluginPath )) {
264+ Path pluginDir = INSTALLED_DIR .resolve (pluginBaseName );
265+ try {
266+ FileUtils .deleteDirIfExists (pluginDir );
267+ org .apache .commons .io .FileUtils .moveDirectory (pluginPath .toFile (), pluginDir .toFile ());
268+ } catch (IOException e ) {
269+ throw new JadxRuntimeException ("Failed to install plugin: " + pluginBaseName , e );
270270 }
271- return false ;
272- });
271+ metadata .setPath (INSTALLED_DIR .relativize (pluginDir ).toString ());
272+ } else {
273+ throw new JadxRuntimeException ("Unexpected plugin path type: " + pluginPathStr );
274+ }
275+ // update plugins json
276+ JadxInstalledPlugins plugins = loadPluginsJson ();
273277 plugins .getInstalled ().add (metadata );
274278 plugins .setUpdated (System .currentTimeMillis ());
275279 savePluginsJson (plugins );
276280 }
277281
278282 private void fillMetadata (JadxPluginMetadata metadata ) {
279283 try {
280- Path tmpJar ;
281- if (needDownload (metadata . getJar () )) {
282- tmpJar = Files . createTempFile ( metadata . getName (), " plugin.jar" );
283- PluginUtils . downloadFile ( metadata . getJar (), tmpJar );
284- metadata . setJar ( tmpJar . toAbsolutePath (). toString () );
285- } else {
286- tmpJar = Paths . get ( metadata . getJar () );
284+ String pluginPath = metadata . getPath () ;
285+ if (needDownload (pluginPath )) {
286+ // download plugin
287+ String ext = CommonFileUtils . getFileExtension ( pluginPath );
288+ Path tmpJar = Files . createTempFile ( metadata . getName (), "plugin." + ext );
289+ PluginUtils . downloadFile ( pluginPath , tmpJar );
290+ pluginPath = tmpJar . toAbsolutePath (). toString ( );
287291 }
288- fillMetadataFromJar (metadata , tmpJar );
292+ if (pluginPath .endsWith (".zip" )) {
293+ // unpack plugin zip
294+ Path tmpDir = Files .createTempDirectory (metadata .getName ());
295+ unzip (Paths .get (pluginPath ), tmpDir );
296+ pluginPath = tmpDir .toAbsolutePath ().toString ();
297+ }
298+ metadata .setPath (pluginPath );
299+ fillMetadataFromPath (metadata , Paths .get (pluginPath ));
289300 } catch (Exception e ) {
290301 throw new RuntimeException ("Failed to fill plugin metadata, plugin: " + metadata .getPluginId (), e );
291302 }
292303 }
293304
294- private void fillMetadataFromJar (JadxPluginMetadata metadata , Path jar ) {
305+ private void fillMetadataFromPath (JadxPluginMetadata metadata , Path pluginPath ) {
295306 try (JadxExternalPluginsLoader loader = new JadxExternalPluginsLoader ()) {
296- JadxPlugin jadxPlugin = loader .loadFromJar ( jar );
307+ JadxPlugin jadxPlugin = loader .loadFromPath ( pluginPath );
297308 JadxPluginInfo pluginInfo = jadxPlugin .getPluginInfo ();
298309 metadata .setPluginId (pluginInfo .getPluginId ());
299310 metadata .setName (pluginInfo .getName ());
@@ -317,9 +328,14 @@ private void copyJar(Path sourceJar, Path destJar) {
317328 }
318329 }
319330
320- private void deletePluginJar (JadxPluginMetadata plugin ) {
331+ private void deletePlugin (JadxPluginMetadata plugin ) {
321332 try {
322- Files .deleteIfExists (INSTALLED_DIR .resolve (plugin .getJar ()));
333+ Path pluginPath = INSTALLED_DIR .resolve (plugin .getPath ());
334+ if (Files .isDirectory (pluginPath )) {
335+ FileUtils .deleteDir (pluginPath );
336+ } else {
337+ Files .deleteIfExists (pluginPath );
338+ }
323339 } catch (IOException e ) {
324340 // ignore
325341 }
@@ -363,11 +379,15 @@ private void upgradePluginsData(JadxInstalledPlugins data) {
363379 }
364380 }
365381
366- private static void collectJarsFromDir (List <Path > list , Path dir ) {
367- try (Stream <Path > files = Files .list (dir )) {
368- files .filter (p -> p .getFileName ().toString ().endsWith (".jar" )).forEach (list ::add );
382+ private static void unzip (Path zipFile , Path outDir ) {
383+ ZipReader zipReader = new ZipReader (); // TODO: pass zip options from jadx args
384+ try (ZipContent content = zipReader .open (zipFile .toFile ())) {
385+ for (IZipEntry entry : content .getEntries ()) {
386+ Path entryFile = outDir .resolve (entry .getName ());
387+ Files .copy (entry .getInputStream (), entryFile , StandardCopyOption .REPLACE_EXISTING );
388+ }
369389 } catch (IOException e ) {
370- throw new RuntimeException ( e );
390+ throw new JadxRuntimeException ( "Failed to unzip file: " + zipFile , e );
371391 }
372392 }
373393}
0 commit comments