package com.amaze.filemanager.filesystem;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.provider.DocumentFile;
import com.amaze.filemanager.exceptions.RootNotPermittedException;
import com.amaze.filemanager.utils.CloudUtil;
import com.amaze.filemanager.utils.Logger;
import com.amaze.filemanager.utils.MainActivityHelper;
import com.amaze.filemanager.utils.OpenMode;
import com.amaze.filemanager.utils.RootUtils;
import com.cloudrail.si.interfaces.CloudStorage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import static com.amaze.filemanager.activities.MainActivity.dataUtils;
/**
* Created by arpitkh996 on 13-01-2016, modified by Emmanuel Messulam<emmanuelbendavid@gmail.com>
*/
public class Operations {
// reserved characters by OS, shall not be allowed in file names
private static final String FOREWARD_SLASH = "/";
private static final String BACKWARD_SLASH = "\\";
private static final String COLON = ":";
private static final String ASTERISK = "*";
private static final String QUESTION_MARK = "?";
private static final String QUOTE = "\"";
private static final String GREATER_THAN = ">";
private static final String LESS_THAN = "<";
private static final String FAT = "FAT";
public interface ErrorCallBack {
/**
* Callback fired when file being created in process already exists
*
* @param file
*/
void exists(HFile file);
/**
* Callback fired when creating new file/directory and required storage access framework permission
* to access SD Card is not available
*
* @param file
*/
void launchSAF(HFile file);
/**
* Callback fired when renaming file and required storage access framework permission to access
* SD Card is not available
*
* @param file
* @param file1
*/
void launchSAF(HFile file, HFile file1);
/**
* Callback fired when we're done processing the operation
*
* @param hFile
* @param b defines whether operation was successful
*/
void done(HFile hFile, boolean b);
/**
* Callback fired when an invalid file name is found.
*
* @param file
*/
void invalidName(HFile file);
}
public static void mkdir(@NonNull final HFile file, final Context context, final boolean rootMode,
@NonNull final ErrorCallBack errorCallBack) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// checking whether filename is valid or a recursive call possible
if (MainActivityHelper.isNewDirectoryRecursive(file) ||
!Operations.isFileNameValid(file.getName(context))) {
errorCallBack.invalidName(file);
return null;
}
if (file.exists()) {
errorCallBack.exists(file);
return null;
}
if (file.isSmb()) {
try {
file.getSmbFile(2000).mkdirs();
} catch (SmbException e) {
Logger.log(e, file.getPath(), context);
errorCallBack.done(file, false);
return null;
}
errorCallBack.done(file, file.exists());
return null;
} else if (file.isOtgFile()) {
// first check whether new directory already exists
DocumentFile directoryToCreate = RootHelper.getDocumentFile(file.getPath(), context, false);
if (directoryToCreate != null) errorCallBack.exists(file);
DocumentFile parentDirectory = RootHelper.getDocumentFile(file.getParent(), context, false);
if (parentDirectory.isDirectory()) {
parentDirectory.createDirectory(file.getName(context));
errorCallBack.done(file, true);
} else errorCallBack.done(file, false);
return null;
} else if (file.isDropBoxFile()) {
CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX);
try {
cloudStorageDropbox.createFolder(CloudUtil.stripPath(OpenMode.DROPBOX, file.getPath()));
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isBoxFile()) {
CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX);
try {
cloudStorageBox.createFolder(CloudUtil.stripPath(OpenMode.BOX, file.getPath()));
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isOneDriveFile()) {
CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE);
try {
cloudStorageOneDrive.createFolder(CloudUtil.stripPath(OpenMode.ONEDRIVE, file.getPath()));
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isGoogleDriveFile()) {
CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE);
try {
cloudStorageGdrive.createFolder(CloudUtil.stripPath(OpenMode.GDRIVE, file.getPath()));
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else {
if (file.isLocal() || file.isRoot()) {
int mode = checkFolder(new File(file.getParent()), context);
if (mode == 2) {
errorCallBack.launchSAF(file);
return null;
}
if (mode == 1 || mode == 0)
FileUtil.mkdir(file.getFile(), context);
if (!file.exists() && rootMode) {
file.setMode(OpenMode.ROOT);
if (file.exists()) errorCallBack.exists(file);
try {
RootUtils.mkDir(file.getParent(context), file.getName(context));
} catch (RootNotPermittedException e) {
Logger.log(e, file.getPath(), context);
}
errorCallBack.done(file, file.exists());
return null;
}
errorCallBack.done(file, file.exists());
return null;
}
errorCallBack.done(file, file.exists());
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void mkfile(@NonNull final HFile file, final Context context, final boolean rootMode,
@NonNull final ErrorCallBack errorCallBack) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// check whether filename is valid or not
if (!Operations.isFileNameValid(file.getName(context))) {
errorCallBack.invalidName(file);
return null;
}
if (file.exists()) {
errorCallBack.exists(file);
return null;
}
if (file.isSmb()) {
try {
file.getSmbFile(2000).createNewFile();
} catch (SmbException e) {
Logger.log(e, file.getPath(), context);
errorCallBack.done(file, false);
return null;
}
errorCallBack.done(file, file.exists());
return null;
} else if (file.isDropBoxFile()) {
CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX);
try {
byte[] tempBytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes);
cloudStorageDropbox.upload(CloudUtil.stripPath(OpenMode.DROPBOX, file.getPath()),
byteArrayInputStream, 0l, true);
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isBoxFile()) {
CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX);
try {
byte[] tempBytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes);
cloudStorageBox.upload(CloudUtil.stripPath(OpenMode.BOX, file.getPath()),
byteArrayInputStream, 0l, true);
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isOneDriveFile()) {
CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE);
try {
byte[] tempBytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes);
cloudStorageOneDrive.upload(CloudUtil.stripPath(OpenMode.ONEDRIVE, file.getPath()),
byteArrayInputStream, 0l, true);
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isGoogleDriveFile()) {
CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE);
try {
byte[] tempBytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes);
cloudStorageGdrive.upload(CloudUtil.stripPath(OpenMode.GDRIVE, file.getPath()),
byteArrayInputStream, 0l, true);
errorCallBack.done(file, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(file, false);
}
} else if (file.isOtgFile()) {
// first check whether new file already exists
DocumentFile fileToCreate = RootHelper.getDocumentFile(file.getPath(), context, false);
if (fileToCreate != null) errorCallBack.exists(file);
DocumentFile parentDirectory = RootHelper.getDocumentFile(file.getParent(), context, false);
if (parentDirectory.isDirectory()) {
parentDirectory.createFile(file.getName(context).substring(file.getName().lastIndexOf(".")),
file.getName(context));
errorCallBack.done(file, true);
} else errorCallBack.done(file, false);
return null;
} else {
if (file.isLocal() || file.isRoot()) {
int mode = checkFolder(new File(file.getParent()), context);
if (mode == 2) {
errorCallBack.launchSAF(file);
return null;
}
if (mode == 1 || mode == 0)
try {
FileUtil.mkfile(file.getFile(), context);
} catch (IOException e) {
}
if (!file.exists() && rootMode) {
file.setMode(OpenMode.ROOT);
if (file.exists()) errorCallBack.exists(file);
try {
RootUtils.mkFile(file.getPath());
} catch (RootNotPermittedException e) {
Logger.log(e, file.getPath(), context);
}
errorCallBack.done(file, file.exists());
return null;
}
errorCallBack.done(file, file.exists());
return null;
}
errorCallBack.done(file, file.exists());
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void rename(final HFile oldFile, final HFile newFile, final boolean rootMode,
final Context context, final ErrorCallBack errorCallBack) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// check whether file names for new file are valid or recursion occurs
if (MainActivityHelper.isNewDirectoryRecursive(newFile) ||
!Operations.isFileNameValid(newFile.getName(context))) {
errorCallBack.invalidName(newFile);
return null;
}
if (newFile.exists()) {
errorCallBack.exists(newFile);
return null;
}
if (oldFile.isSmb()) {
try {
SmbFile smbFile = new SmbFile(oldFile.getPath());
SmbFile smbFile1 = new SmbFile(newFile.getPath());
if (smbFile1.exists()) {
errorCallBack.exists(newFile);
return null;
}
smbFile.renameTo(smbFile1);
if (!smbFile.exists() && smbFile1.exists())
errorCallBack.done(newFile, true);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (SmbException e) {
e.printStackTrace();
}
return null;
} else if (oldFile.isDropBoxFile()) {
CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX);
try {
cloudStorageDropbox.move(CloudUtil.stripPath(OpenMode.DROPBOX, oldFile.getPath()),
CloudUtil.stripPath(OpenMode.DROPBOX, newFile.getPath()));
errorCallBack.done(newFile, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(newFile, false);
}
} else if (oldFile.isBoxFile()) {
CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX);
try {
cloudStorageBox.move(CloudUtil.stripPath(OpenMode.BOX, oldFile.getPath()),
CloudUtil.stripPath(OpenMode.BOX, newFile.getPath()));
errorCallBack.done(newFile, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(newFile, false);
}
} else if (oldFile.isOneDriveFile()) {
CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE);
try {
cloudStorageOneDrive.move(CloudUtil.stripPath(OpenMode.ONEDRIVE, oldFile.getPath()),
CloudUtil.stripPath(OpenMode.ONEDRIVE, newFile.getPath()));
errorCallBack.done(newFile, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(newFile, false);
}
} else if (oldFile.isGoogleDriveFile()) {
CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE);
try {
cloudStorageGdrive.move(CloudUtil.stripPath(OpenMode.GDRIVE, oldFile.getPath()),
CloudUtil.stripPath(OpenMode.GDRIVE, newFile.getPath()));
errorCallBack.done(newFile, true);
} catch (Exception e) {
e.printStackTrace();
errorCallBack.done(newFile, false);
}
} else if (oldFile.isOtgFile()) {
DocumentFile oldDocumentFile = RootHelper.getDocumentFile(oldFile.getPath(), context, false);
DocumentFile newDocumentFile = RootHelper.getDocumentFile(newFile.getPath(), context, false);
if (newDocumentFile != null) {
errorCallBack.exists(newFile);
return null;
}
errorCallBack.done(newFile, oldDocumentFile.renameTo(newFile.getName(context)));
return null;
} else {
File file = new File(oldFile.getPath());
File file1 = new File(newFile.getPath());
switch (oldFile.getMode()) {
case FILE:
int mode = checkFolder(file.getParentFile(), context);
if (mode == 2) {
errorCallBack.launchSAF(oldFile, newFile);
} else if (mode == 1 || mode == 0) {
try {
FileUtil.renameFolder(file, file1, context);
} catch (RootNotPermittedException e) {
e.printStackTrace();
}
boolean a = !file.exists() && file1.exists();
if (!a && rootMode) {
try {
RootUtils.rename(file.getPath(), file1.getPath());
} catch (Exception e) {
Logger.log(e, oldFile.getPath() + "\n" + newFile.getPath(), context);
}
oldFile.setMode(OpenMode.ROOT);
newFile.setMode(OpenMode.ROOT);
a = !file.exists() && file1.exists();
}
errorCallBack.done(newFile, a);
return null;
}
break;
case ROOT:
try {
RootUtils.rename(file.getPath(), file1.getPath());
} catch (Exception e) {
Logger.log(e, oldFile.getPath() + "\n" + newFile.getPath(), context);
}
newFile.setMode(OpenMode.ROOT);
errorCallBack.done(newFile, true);
break;
}
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private static int checkFolder(final File folder, Context context) {
boolean lol = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
if (lol) {
boolean ext = FileUtil.isOnExtSdCard(folder, context);
if (ext) {
if (!folder.exists() || !folder.isDirectory()) {
return 0;
}
// On Android 5, trigger storage access framework.
if (!FileUtil.isWritableNormalOrSaf(folder, context)) {
return 2;
}
return 1;
}
} else if (Build.VERSION.SDK_INT == 19) {
// Assume that Kitkat workaround works
if (FileUtil.isOnExtSdCard(folder, context)) return 1;
}
// file not on external sd card
if (FileUtil.isWritable(new File(folder, "DummyFile"))) {
return 1;
} else {
return 0;
}
}
/**
* Well, we wouldn't want to copy when the target is inside the source
* otherwise it'll end into a loop
*
* @param sourceFile
* @param targetFile
* @return true when copy loop is possible
*/
public static boolean isCopyLoopPossible(BaseFile sourceFile, HFile targetFile) {
return targetFile.getPath().contains(sourceFile.getPath());
}
/**
* Validates file name
* special reserved characters shall not be allowed in the file names on FAT filesystems
*
* @param fileName the filename, not the full path!
* @return boolean if the file name is valid or invalid
*/
public static boolean isFileNameValid(String fileName) {
//String fileName = builder.substring(builder.lastIndexOf("/")+1, builder.length());
// TODO: check file name validation only for FAT filesystems
return !(fileName.contains(ASTERISK) || fileName.contains(BACKWARD_SLASH) ||
fileName.contains(COLON) || fileName.contains(FOREWARD_SLASH) ||
fileName.contains(GREATER_THAN) || fileName.contains(LESS_THAN) ||
fileName.contains(QUESTION_MARK) || fileName.contains(QUOTE));
}
private static boolean isFileSystemFAT(String mountPoint) {
String[] args = new String[]{"/bin/bash", "-c", "df -DO_NOT_REPLACE | awk '{print $1,$2,$NF}' | grep \"^"
+ mountPoint + "\""};
try {
Process proc = new ProcessBuilder(args).start();
OutputStream outputStream = proc.getOutputStream();
String buffer = null;
outputStream.write(buffer.getBytes());
return buffer != null && buffer.contains(FAT);
} catch (IOException e) {
e.printStackTrace();
// process interrupted, returning true, as a word of cation
return true;
}
}
}