#include #include #include #include #include #include #include #include "jni.hpp" #include "libsmb2.h" namespace { class Dir { public: Dir(std::shared_ptr context, smb2dir* dir) : context_(std::move(context)), dir_(dir) { assert(context_ && dir_); } ~Dir() { smb2_closedir(context_.get(), dir_); } Dir(const Dir&) = delete; Dir& operator=(const Dir&) = delete; private: std::shared_ptr context_; smb2dir* const dir_; }; class Url { public: explicit Url(smb2_url* url) : url_(url) { assert(url_); } ~Url() { smb2_destroy_url(url_); } Url(const Url&) = delete; Url& operator=(const Url&) = delete; [[nodiscard]] const char* path() const { return url_->path; } [[nodiscard]] const char* server() const { return url_->server; } [[nodiscard]] const char* share() const { return url_->share; } [[nodiscard]] const char* user() const { return url_->user; } private: smb2_url* url_; }; class Context { public: explicit Context(int timeout) : context_(smb2_init_context(), ContextDeleter{}) { smb2_set_timeout(context_.get(), timeout); } ~Context() = default; Context(const Context&) = delete; Context& operator=(const Context&) = delete; [[nodiscard]] std::unique_ptr ParseUrl(const std::string& url) { auto* ptr = smb2_parse_url(context_.get(), url.c_str()); return ptr ? std::make_unique(ptr): nullptr; } bool Connect(const Url& url, const std::optional& username, const std::optional& password) { if (password.has_value()) smb2_set_password(context_.get(), password->c_str()); auto* user = username.has_value() ? username->c_str() : url.user(); return smb2_connect_share(context_.get(), url.server(), url.share(), user) == 0; } [[nodiscard]] std::string_view GetError() { return smb2_get_error(context_.get()); } [[nodiscard]] std::unique_ptr OpenDir(const std::string& path) { auto* ptr = smb2_opendir(context_.get(), path.c_str()); return ptr ? std::make_unique(context_, ptr) : nullptr; } private: struct ContextDeleter { void operator()(smb2_context* context) { smb2_destroy_context(context); } }; std::shared_ptr context_; }; jlong nativeContextNew(JNIEnv* env, jclass clazz, jint timeout) { return reinterpret_cast(new Context(static_cast(timeout))); } void nativeContextDestroy(JNIEnv* env, jclass clazz, jlong ptr) { delete reinterpret_cast(ptr); } jlong nativeContextParseUrl(JNIEnv* env, jclass clazz, jlong ptr, jstring url) { return reinterpret_cast(reinterpret_cast(ptr)->ParseUrl(jni::StringToUTF8(env, jni::ParamRef(env, url))).release()); } jboolean nativeContextConnect(JNIEnv* env, jclass clazz, jlong context_ptr, jlong url_ptr, jstring j_username, jstring j_password) { auto* url = reinterpret_cast(url_ptr); if (!url) return JNI_FALSE; std::optional username; std::optional password; if (j_username) username = jni::StringToUTF8(env, jni::ParamRef(env, j_username)); if (j_password) password = jni::StringToUTF8(env, jni::ParamRef(env, j_password)); return reinterpret_cast(context_ptr)->Connect(*url, username, password) ? JNI_TRUE : JNI_FALSE; } jstring nativeContextGetError(JNIEnv* env, jclass clazz, jlong ptr) { return jni::UTF8ToString(env, std::string(reinterpret_cast(ptr)->GetError())).release(); } jlong nativeContextOpenDir(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { return reinterpret_cast(reinterpret_cast(ptr)->OpenDir(jni::StringToUTF8(env, jni::ParamRef(env, path))).release()); } void nativeUrlDestroy(JNIEnv* env, jclass clazz, jlong ptr) { delete reinterpret_cast(ptr); } jstring nativeUrlPath(JNIEnv* env, jclass clazz, jlong ptr) { return jni::UTF8ToString(env, std::string(reinterpret_cast(ptr)->path())).release(); } void nativeDirDestroy(JNIEnv* env, jclass clazz, jlong ptr) { delete reinterpret_cast(ptr); } void RegisterSamba(JNIEnv* env) { auto clazz = jni::FindClass(env, "org/the_jk/cleversync/io/samba/NativeSamba"); ABORT_IF_NULL(env, clazz); static const JNINativeMethod methods[] = { { "nativeContextNew", "(I)J", reinterpret_cast(&nativeContextNew) }, { "nativeContextDestroy", "(J)V", reinterpret_cast(&nativeContextDestroy) }, { "nativeContextParseUrl", "(JLjava/lang/String;)J", reinterpret_cast(&nativeContextParseUrl) }, { "nativeContextConnect", "(JJLjava/lang/String;Ljava/lang/String;)Z", reinterpret_cast(&nativeContextConnect) }, { "nativeContextGetError", "(J)Ljava/lang/String;", reinterpret_cast(&nativeContextGetError) }, { "nativeContextOpenDir", "(JLjava/lang/String;)J", reinterpret_cast(&nativeContextOpenDir) }, { "nativeUrlDestroy", "(J)V", reinterpret_cast(&nativeUrlDestroy) }, { "nativeUrlPath", "(J)Ljava/lang/String;", reinterpret_cast(&nativeUrlPath) }, { "nativeDirDestroy", "(J)V", reinterpret_cast(&nativeDirDestroy) }, }; auto ret = env->RegisterNatives(clazz.get(), methods, sizeof(methods) / sizeof(methods[0])); ABORT_IF_NOT_OK(ret); } } // namespace jint JNI_OnLoad(JavaVM *vm, void *reserved) { auto* env = jni::OnLoad(vm); RegisterSamba(env); return jni::JNI_VERSION; }