@ -1,30 +1,35 @@
package com.fox2code.mmm.utils ;
package com.fox2code.mmm.utils ;
import static androidx.fragment.app.FragmentManager.TAG ;
import android.annotation.SuppressLint ;
import android.net.Uri ;
import android.net.Uri ;
import android.os.Bundle ;
import android.os.Bundle ;
import android.util.Log ;
import android.util.Log ;
import android.widget.Toast ;
import android.widget.Toast ;
import androidx.appcompat.app.AlertDialog ;
import com.fox2code.foxcompat.app.FoxActivity ;
import com.fox2code.foxcompat.app.FoxActivity ;
import com.fox2code.mmm.BuildConfig ;
import com.fox2code.mmm.BuildConfig ;
import com.fox2code.mmm.R ;
import com.fox2code.mmm.R ;
import com.fox2code.mmm.installer.InstallerInitializer ;
import com.fox2code.mmm.installer.InstallerInitializer ;
import com.google.android.material.dialog.MaterialAlertDialogBuilder ;
import java.io.File ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.FileOutputStream ;
import java.io.IOException ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.InputStream ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipFile ;
public class ZipFileOpener extends FoxActivity {
public class ZipFileOpener extends FoxActivity {
AlertDialog loading = null ;
// Adds us as a handler for zip files, so we can pass them to the installer
// Adds us as a handler for zip files, so we can pass them to the installer
// We should have a content uri provided to us.
// We should have a content uri provided to us.
@SuppressLint ( "RestrictedApi" )
@Override
@Override
protected void onCreate ( Bundle savedInstanceState ) {
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
super . onCreate ( savedInstanceState ) ;
loading = BudgetProgressDialog . build ( this , R . string . loading , R . string . zip_unpacking ) ;
new Thread ( ( ) - > {
if ( BuildConfig . DEBUG ) {
if ( BuildConfig . DEBUG ) {
Log . d ( "ZipFileOpener" , "onCreate: " + getIntent ( ) ) ;
Log . d ( "ZipFileOpener" , "onCreate: " + getIntent ( ) ) ;
}
}
@ -32,18 +37,28 @@ public class ZipFileOpener extends FoxActivity {
Uri uri = getIntent ( ) . getData ( ) ;
Uri uri = getIntent ( ) . getData ( ) ;
if ( uri = = null ) {
if ( uri = = null ) {
Log . e ( "ZipFileOpener" , "onCreate: No data provided" ) ;
Log . e ( "ZipFileOpener" , "onCreate: No data provided" ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finish ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
return ;
return ;
}
}
// Try to copy the file to our cache
// Try to copy the file to our cache
try {
try {
// check if its a file over 10MB
Long fileSize = Files . getFileSize ( this , uri ) ;
if ( fileSize = = null ) fileSize = 0 L ;
if ( 1000L * 1000 * 10 < fileSize ) {
runOnUiThread ( ( ) - > loading . show ( ) ) ;
}
zipFile = File . createTempFile ( "module" , ".zip" , getCacheDir ( ) ) ;
zipFile = File . createTempFile ( "module" , ".zip" , getCacheDir ( ) ) ;
try ( InputStream inputStream = getContentResolver ( ) . openInputStream ( uri ) ; FileOutputStream outputStream = new FileOutputStream ( zipFile ) ) {
try ( InputStream inputStream = getContentResolver ( ) . openInputStream ( uri ) ; FileOutputStream outputStream = new FileOutputStream ( zipFile ) ) {
if ( inputStream = = null ) {
if ( inputStream = = null ) {
Log . e ( TAG , "onCreate: Failed to open input stream" ) ;
Log . e ( "ZipFileOpener" , "onCreate: Failed to open input stream" ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
return ;
return ;
}
}
byte [ ] buffer = new byte [ 4096 ] ;
byte [ ] buffer = new byte [ 4096 ] ;
@ -54,42 +69,96 @@ public class ZipFileOpener extends FoxActivity {
}
}
} catch (
} catch (
Exception e ) {
Exception e ) {
Log . e ( TAG , "onCreate: Failed to copy zip file" , e ) ;
Log . e ( "ZipFileOpener" , "onCreate: Failed to copy zip file" , e ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
return ;
return ;
}
}
// Ensure zip is not empty
// Ensure zip is not empty
if ( zipFile . length ( ) = = 0 ) {
if ( zipFile . length ( ) = = 0 ) {
Log . e ( TAG , "onCreate: Zip file is empty" ) ;
Log . e ( "ZipFileOpener" , "onCreate: Zip file is empty" ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
return ;
return ;
} else {
} else {
if ( BuildConfig . DEBUG ) {
if ( BuildConfig . DEBUG ) {
Log . d ( "ZipFileOpener" , "onCreate: Zip file is " + zipFile . length ( ) + " bytes" ) ;
Log . d ( "ZipFileOpener" , "onCreate: Zip file is " + zipFile . length ( ) + " bytes" ) ;
}
}
}
}
ZipEntry entry ;
ZipFile zip = null ;
// Unpack the zip to validate it's a valid magisk module
// Unpack the zip to validate it's a valid magisk module
// It needs to have, at the bare minimum, a module.prop file. Everything else is technically optional.
// It needs to have, at the bare minimum, a module.prop file. Everything else is technically optional.
// First, check if it's a zip file
// First, check if it's a zip file
try ( java . util . zip . ZipFile zip = new java . util . zip . ZipFile ( zipFile ) ) {
try {
if ( zip . getEntry ( "module.prop" ) = = null ) {
zip = new ZipFile ( zipFile ) ;
Log . e ( TAG , "onCreate: Zip file is not a valid magisk module" ) ;
if ( ( entry = zip . getEntry ( "module.prop" ) ) = = null ) {
Log . e ( "ZipFileOpener" , "onCreate: Zip file is not a valid magisk module" ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . invalid_format , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . invalid_format , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
return ;
return ;
}
}
} catch (
} catch (
IOException e ) {
Exception e ) {
Log . e ( TAG , "onCreate: Failed to open zip file" , e ) ;
Log . e ( "ZipFileOpener" , "onCreate: Failed to open zip file" , e ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
Toast . makeText ( this , R . string . zip_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
if ( zip ! = null ) {
try {
zip . close ( ) ;
} catch ( IOException exception ) {
Log . e ( "ZipFileOpener" , Log . getStackTraceString ( exception ) ) ;
}
}
return ;
return ;
}
}
if ( BuildConfig . DEBUG ) {
if ( BuildConfig . DEBUG ) {
Log . d ( "ZipFileOpener" , "onCreate: Zip file is valid" ) ;
Log . d ( "ZipFileOpener" , "onCreate: Zip file is valid" ) ;
}
}
String moduleInfo ;
try {
moduleInfo = PropUtils . readModulePropSimple ( zip . getInputStream ( entry ) , "name" ) ;
if ( moduleInfo = = null ) {
throw new NullPointerException ( "moduleInfo is null, check earlier logs for root cause" ) ;
}
} catch (
Exception e ) {
Log . e ( "ZipFileOpener" , "onCreate: Failed to load module id" , e ) ;
runOnUiThread ( ( ) - > {
Toast . makeText ( this , R . string . zip_prop_load_failed , Toast . LENGTH_LONG ) . show ( ) ;
finishAndRemoveTask ( ) ;
} ) ;
try {
zip . close ( ) ;
} catch ( IOException exception ) {
Log . e ( "ZipFileOpener" , Log . getStackTraceString ( exception ) ) ;
}
return ;
}
try {
zip . close ( ) ;
} catch ( IOException exception ) {
Log . e ( "ZipFileOpener" , Log . getStackTraceString ( exception ) ) ;
}
runOnUiThread ( ( ) - > {
new MaterialAlertDialogBuilder ( this )
. setTitle ( R . string . zip_security_warning )
. setMessage ( getString ( R . string . zip_intent_module_install , moduleInfo , Files . getFileName ( this , uri ) ) )
. setCancelable ( false )
. setNegativeButton ( R . string . no , ( d , i ) - > {
d . dismiss ( ) ;
finishAndRemoveTask ( ) ;
} )
. setPositiveButton ( R . string . yes , ( d , i ) - > {
d . dismiss ( ) ;
// Pass the file to the installer
// Pass the file to the installer
FoxActivity compatActivity = FoxActivity . getFoxActivity ( this ) ;
FoxActivity compatActivity = FoxActivity . getFoxActivity ( this ) ;
IntentHelper . openInstaller ( compatActivity , zipFile . getAbsolutePath ( ) ,
IntentHelper . openInstaller ( compatActivity , zipFile . getAbsolutePath ( ) ,
@ -97,5 +166,11 @@ public class ZipFileOpener extends FoxActivity {
R . string . local_install_title ) , null , null , false ,
R . string . local_install_title ) , null , null , false ,
BuildConfig . DEBUG & & // Use debug mode if no root
BuildConfig . DEBUG & & // Use debug mode if no root
InstallerInitializer . peekMagiskPath ( ) = = null ) ;
InstallerInitializer . peekMagiskPath ( ) = = null ) ;
finish ( ) ;
} )
. show ( ) ;
loading . dismiss ( ) ;
} ) ;
} ) . start ( ) ;
}
}
}
}