|
|
|
|
@ -43,6 +43,7 @@
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <strings.h> |
|
|
|
|
#include <stdarg.h> |
|
|
|
|
#include <limits.h> |
|
|
|
|
#include <unistd.h> |
|
|
|
|
@ -2961,4 +2962,89 @@ invalid:
|
|
|
|
|
u_free(req); return JNI_FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Общие файловые команды. Wire уже UTF-8, поэтому JNI modified UTF-8 не портит имена с emoji. */ |
|
|
|
|
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileRequest( |
|
|
|
|
JNIEnv* env, jobject thiz, jlong group, jlong server, jbyteArray wire) { |
|
|
|
|
(void)thiz; |
|
|
|
|
struct UTUN_INSTANCE* inst = instance_lite_get_instance(); |
|
|
|
|
struct UASYNC* ua = instance_lite_get_uasync(); |
|
|
|
|
jsize size = wire ? (*env)->GetArrayLength(env, wire) : 0; |
|
|
|
|
struct mf_gui_request* q = NULL; |
|
|
|
|
if (!inst || !ua || !uasync_is_running(ua) || !group || !server || size < MF_WIRE_HEADER || size > MF_WIRE_MAX) goto invalid; |
|
|
|
|
uint8_t bytes[MF_WIRE_MAX]; |
|
|
|
|
(*env)->GetByteArrayRegion(env, wire, 0, size, (jbyte*)bytes); |
|
|
|
|
if ((*env)->ExceptionCheck(env)) goto invalid; |
|
|
|
|
q = u_calloc(1, sizeof(*q)); |
|
|
|
|
if (!q || mf_decode(bytes, size, &q->message)) goto invalid; |
|
|
|
|
struct mf_message* m = &q->message; |
|
|
|
|
int read = m->command == MF_LIST || m->command == MF_STAT || m->command == MF_QUOTA; |
|
|
|
|
int write = m->command == MF_MKDIR || m->command == MF_RENAME || m->command == MF_REMOVE_PATH; |
|
|
|
|
if (m->kind != MF_REQUEST || (!read && !write) || m->data_len || m->offset || m->size || m->more || |
|
|
|
|
!mf_path_valid(m->path, read) || (!m->owner && (m->command != MF_LIST || m->path[0])) || |
|
|
|
|
(m->command == MF_RENAME ? !mf_path_valid(m->target, 0) : m->target[0] != 0)) goto invalid; |
|
|
|
|
if (write && (m->owner != inst->node_id || |
|
|
|
|
(!strncasecmp(m->path, "avatars", 7) && (!m->path[7] || m->path[7] == '/')))) goto invalid; |
|
|
|
|
if (m->command == MF_RENAME && !strncasecmp(m->target, "avatars", 7) && (!m->target[7] || m->target[7] == '/')) goto invalid; |
|
|
|
|
q->inst = inst; q->group = (uint64_t)group; q->server = (uint64_t)server; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "Android files: request id=%llu cmd=%s group=%llu server=%llu owner=%llu path=%s", |
|
|
|
|
(unsigned long long)m->request_id, mf_command_text(m->command), (unsigned long long)group, |
|
|
|
|
(unsigned long long)server, (unsigned long long)m->owner, m->path); |
|
|
|
|
uasync_post(ua, member_files_gui_request, q); return JNI_TRUE; |
|
|
|
|
invalid: |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: request rejected group=%llu server=%llu bytes=%d ready=%d", |
|
|
|
|
(unsigned long long)group, (unsigned long long)server, (int)size, inst && ua && uasync_is_running(ua)); |
|
|
|
|
u_free(q); return JNI_FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Копирование настоящего UTF-8 пути с проверкой длины и отсутствия NUL. */ |
|
|
|
|
static int android_file_path(JNIEnv* env, jbyteArray bytes, char* out) { |
|
|
|
|
jsize size = bytes ? (*env)->GetArrayLength(env, bytes) : 0; |
|
|
|
|
if (size < 1 || size > MF_PATH_MAX) return -1; |
|
|
|
|
(*env)->GetByteArrayRegion(env, bytes, 0, size, (jbyte*)out); |
|
|
|
|
if ((*env)->ExceptionCheck(env) || memchr(out, 0, size)) return -1; |
|
|
|
|
out[size] = 0; return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Handle/локальный файл принадлежат общему transfer, Kotlin ждёт его терминальное событие. */ |
|
|
|
|
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileTransfer( |
|
|
|
|
JNIEnv* env, jobject thiz, jlong id, jlong group, jlong server, jlong owner, |
|
|
|
|
jbyteArray remote, jbyteArray local, jboolean upload, jbyteArray version) { |
|
|
|
|
(void)thiz; |
|
|
|
|
struct UTUN_INSTANCE* inst = instance_lite_get_instance(); |
|
|
|
|
struct UASYNC* ua = instance_lite_get_uasync(); |
|
|
|
|
struct mf_transfer_request* q = NULL; |
|
|
|
|
if (!inst || !ua || !uasync_is_running(ua) || !id || !group || !server || !owner || |
|
|
|
|
!version || (*env)->GetArrayLength(env, version) != 16) goto invalid; |
|
|
|
|
q = u_calloc(1, sizeof(*q)); |
|
|
|
|
if (!q || android_file_path(env, remote, q->remote) || android_file_path(env, local, q->local) || |
|
|
|
|
!mf_path_valid(q->remote, 0) || q->local[0] != '/') goto invalid; |
|
|
|
|
(*env)->GetByteArrayRegion(env, version, 0, 16, (jbyte*)q->version); |
|
|
|
|
if ((*env)->ExceptionCheck(env) || (!upload && mf_zero(q->version, 16))) goto invalid; |
|
|
|
|
if (upload && (owner != (jlong)inst->node_id || |
|
|
|
|
(!strncasecmp(q->remote, "avatars", 7) && (!q->remote[7] || q->remote[7] == '/')))) goto invalid; |
|
|
|
|
q->inst = inst; q->id = id; q->group = group; q->server = server; q->owner = owner; q->upload = upload != JNI_FALSE; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "Android files: transfer queued id=%llu group=%llu server=%llu owner=%llu upload=%d path=%s", |
|
|
|
|
(unsigned long long)id, (unsigned long long)group, (unsigned long long)server, (unsigned long long)owner, q->upload, q->remote); |
|
|
|
|
uasync_post(ua, member_files_gui_transfer, q); return JNI_TRUE; |
|
|
|
|
invalid: |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: transfer rejected id=%llu group=%llu server=%llu owner=%llu upload=%d", |
|
|
|
|
(unsigned long long)id, (unsigned long long)group, (unsigned long long)server, (unsigned long long)owner, upload); |
|
|
|
|
u_free(q); return JNI_FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Отмена по уникальному ID не затрагивает передачи других владельцев. */ |
|
|
|
|
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileCancel(JNIEnv* env, jobject thiz, jlong id) { |
|
|
|
|
(void)env; (void)thiz; |
|
|
|
|
struct UTUN_INSTANCE* inst = instance_lite_get_instance(); |
|
|
|
|
struct UASYNC* ua = instance_lite_get_uasync(); |
|
|
|
|
if (!id || !inst || !ua || !uasync_is_running(ua)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "Android files: cancel unavailable id=%llu", (unsigned long long)id); return JNI_FALSE; |
|
|
|
|
} |
|
|
|
|
struct mf_cancel_request* q = u_calloc(1, sizeof(*q)); |
|
|
|
|
if (!q) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: cancel allocation failed"); return JNI_FALSE; } |
|
|
|
|
q->inst = inst; q->id = id; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "Android files: cancel queued id=%llu", (unsigned long long)id); |
|
|
|
|
uasync_post(ua, member_files_gui_cancel, q); return JNI_TRUE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif /* __ANDROID__ */ |
|
|
|
|
|