From e4d82685ffcece466cb6dd65a476b897ad1b75af Mon Sep 17 00:00:00 2001 From: Stuart Scott Date: Tue, 3 Nov 2020 06:31:02 -0800 Subject: [PATCH] Use file ops for file:// and document ops for content:// (#1495) --- internal/driver/gomobile/android.c | 120 ++++++++++++++++++++- internal/driver/gomobile/folder.go | 4 +- internal/driver/gomobile/folder_android.go | 12 +-- internal/driver/gomobile/folder_desktop.go | 4 +- internal/driver/gomobile/folder_ios.go | 4 +- 5 files changed, 128 insertions(+), 16 deletions(-) diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index a22f746e79..fe3e583120 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -1,10 +1,12 @@ // +build android #include +#include #include #include #include #include +#include #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Fyne", __VA_ARGS__) @@ -155,11 +157,26 @@ void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream) { (*env)->DeleteGlobalRef(env, stream); } -bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { +bool hasPrefix(char* string, char* prefix) { + size_t lp = strlen(prefix); + size_t ls = strlen(string); + if (ls < lp) { + return false; + } + return memcmp(prefix, string, lp) == 0; +} + +bool canListContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); jstring uriStr = (*env)->NewStringUTF(env, uriCstr); jobject uri = parseURI(jni_env, ctx, uriCstr); + jthrowable loadErr = (*env)->ExceptionOccurred(env); + + if (loadErr != NULL) { + (*env)->ExceptionClear(env); + return false; + } jclass contractClass = find_class(env, "android/provider/DocumentsContract"); if (contractClass == NULL) { // API 19 @@ -186,11 +203,43 @@ bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { return strcmp(str, "vnd.android.document/directory") == 0; } -char* uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { +bool canListFileURI(char* uriCstr) { + // Get file path from URI + size_t length = strlen(uriCstr)-7;// -7 for 'file://' + char* path = malloc(sizeof(char)*(length+1));// +1 for '\0' + memcpy(path, &uriCstr[7], length); + path[length] = '\0'; + + // Stat path to determine if it points to a directory + struct stat statbuf; + int result = stat(path, &statbuf); + + free(path); + + return (result == 0) && S_ISDIR(statbuf.st_mode); +} + +bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { + if (hasPrefix(uriCstr, "file://")) { + return canListFileURI(uriCstr); + } else if (hasPrefix(uriCstr, "content://")) { + return canListContentURI(jni_env, ctx, uriCstr); + } + LOG_FATAL("Unrecognized scheme: %s", uriCstr); + return false; +} + +char* listContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); jstring uriStr = (*env)->NewStringUTF(env, uriCstr); jobject uri = parseURI(jni_env, ctx, uriCstr); + jthrowable loadErr = (*env)->ExceptionOccurred(env); + + if (loadErr != NULL) { + (*env)->ExceptionClear(env); + return ""; + } jclass contractClass = find_class(env, "android/provider/DocumentsContract"); if (contractClass == NULL) { // API 19 @@ -237,11 +286,74 @@ char* uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { if (old != NULL) { strcpy(ret, old); free(old); + } else { + ret[0] = '\0'; } strcat(ret, uid); strcat(ret, "|"); } - ret[len-1] = '\0'; + if (ret != NULL) { + ret[len-1] = '\0'; + } + return ret; +} + +char* listFileURI(char* uriCstr) { + + size_t uriLength = strlen(uriCstr); + + // Get file path from URI + size_t length = uriLength-7;// -7 for 'file://' + char* path = malloc(sizeof(char)*(length+1));// +1 for '\0' + memcpy(path, &uriCstr[7], length); + path[length] = '\0'; + + char *ret = NULL; + DIR *dfd; + if ((dfd = opendir(path)) != NULL) { + struct dirent *dp; + int len = 0; + while ((dp = readdir(dfd)) != NULL) { + if (strcmp(dp->d_name, ".") == 0) { + // Ignore current directory + continue; + } + if (strcmp(dp->d_name, "..") == 0) { + // Ignore parent directory + continue; + } + // append + char *old = ret; + len = len + uriLength + 1 /* / */ + strlen(dp->d_name) + 1 /* | */; + ret = malloc(sizeof(char)*(len+1)); + if (old != NULL) { + strcpy(ret, old); + free(old); + } else { + ret[0] = '\0'; + } + strcat(ret, uriCstr); + strcat(ret, "/"); + strcat(ret, dp->d_name); + strcat(ret, "|"); + } + if (ret != NULL) { + ret[len-1] = '\0'; + } + } + + free(path); + return ret; -} \ No newline at end of file +} + +char* listURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { + if (hasPrefix(uriCstr, "file://")) { + return listFileURI(uriCstr); + } else if (hasPrefix(uriCstr, "content://")) { + return listContentURI(jni_env, ctx, uriCstr); + } + LOG_FATAL("Unrecognized scheme: %s", uriCstr); + return ""; +} diff --git a/internal/driver/gomobile/folder.go b/internal/driver/gomobile/folder.go index fbb8fdab0d..1d098039c9 100644 --- a/internal/driver/gomobile/folder.go +++ b/internal/driver/gomobile/folder.go @@ -11,11 +11,11 @@ type lister struct { } func (l *lister) List() ([]fyne.URI, error) { - return uriList(l) + return listURI(l) } func (d *mobileDriver) ListerForURI(uri fyne.URI) (fyne.ListableURI, error) { - if !uriCanList(uri) { + if !canListURI(uri) { return nil, fmt.Errorf("specified URI is not listable") } diff --git a/internal/driver/gomobile/folder_android.go b/internal/driver/gomobile/folder_android.go index 70e4fb0242..34c010f734 100644 --- a/internal/driver/gomobile/folder_android.go +++ b/internal/driver/gomobile/folder_android.go @@ -8,8 +8,8 @@ package gomobile #include #include -bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); -char *uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); +bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); +char *listURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); */ import "C" import ( @@ -22,26 +22,26 @@ import ( "fyne.io/fyne/storage" ) -func uriCanList(uri fyne.URI) bool { +func canListURI(uri fyne.URI) bool { uriStr := C.CString(uri.String()) defer C.free(unsafe.Pointer(uriStr)) listable := false app.RunOnJVM(func(_, env, ctx uintptr) error { - listable = bool(C.uriCanList(C.uintptr_t(env), C.uintptr_t(ctx), uriStr)) + listable = bool(C.canListURI(C.uintptr_t(env), C.uintptr_t(ctx), uriStr)) return nil }) return listable } -func uriList(uri fyne.URI) ([]fyne.URI, error) { +func listURI(uri fyne.URI) ([]fyne.URI, error) { uriStr := C.CString(uri.String()) defer C.free(unsafe.Pointer(uriStr)) var str *C.char app.RunOnJVM(func(_, env, ctx uintptr) error { - str = C.uriList(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) + str = C.listURI(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) return nil }) diff --git a/internal/driver/gomobile/folder_desktop.go b/internal/driver/gomobile/folder_desktop.go index 5456464d73..72218bc25f 100644 --- a/internal/driver/gomobile/folder_desktop.go +++ b/internal/driver/gomobile/folder_desktop.go @@ -4,10 +4,10 @@ package gomobile import "fyne.io/fyne" -func uriCanList(fyne.URI) bool { +func canListURI(fyne.URI) bool { return false } -func uriList(fyne.URI) ([]fyne.URI, error) { +func listURI(fyne.URI) ([]fyne.URI, error) { return nil, nil } diff --git a/internal/driver/gomobile/folder_ios.go b/internal/driver/gomobile/folder_ios.go index 824e318048..22e9772020 100644 --- a/internal/driver/gomobile/folder_ios.go +++ b/internal/driver/gomobile/folder_ios.go @@ -21,14 +21,14 @@ import ( "fyne.io/fyne/storage" ) -func uriCanList(uri fyne.URI) bool { +func canListURI(uri fyne.URI) bool { uriStr := C.CString(uri.String()) defer C.free(unsafe.Pointer(uriStr)) return bool(C.iosCanList(uriStr)) } -func uriList(uri fyne.URI) ([]fyne.URI, error) { +func listURI(uri fyne.URI) ([]fyne.URI, error) { uriStr := C.CString(uri.String()) defer C.free(unsafe.Pointer(uriStr))