From 76b755a829ca9f7f216db17b72a940496ca3baa1 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Tue, 8 Jul 2025 08:47:31 +0200 Subject: [PATCH] fix exception handling in hostFileNative --- app/src/main/cpp/CoreWrapper.cpp | 283 ++++++++++++++++--------------- 1 file changed, 148 insertions(+), 135 deletions(-) diff --git a/app/src/main/cpp/CoreWrapper.cpp b/app/src/main/cpp/CoreWrapper.cpp index 70d6868da074..af18b6ac56d9 100644 --- a/app/src/main/cpp/CoreWrapper.cpp +++ b/app/src/main/cpp/CoreWrapper.cpp @@ -17,61 +17,61 @@ namespace { -std::string convertString(JNIEnv *env, jstring string) { - jboolean isCopy; - const char *cstring = env->GetStringUTFChars(string, &isCopy); - auto cppstring = std::string(cstring, env->GetStringUTFLength(string)); - env->ReleaseStringUTFChars(string, cstring); - return cppstring; -} + std::string convertString(JNIEnv *env, jstring string) { + jboolean isCopy; + const char *cstring = env->GetStringUTFChars(string, &isCopy); + auto cppstring = std::string(cstring, env->GetStringUTFLength(string)); + env->ReleaseStringUTFChars(string, cstring); + return cppstring; + } -std::string getStringField(JNIEnv *env, jclass clazz, jobject object, const char *name) { - jfieldID field = env->GetFieldID(clazz, name, "Ljava/lang/String;"); - auto string = (jstring) env->GetObjectField(object, field); - return convertString(env, string); -} + std::string getStringField(JNIEnv *env, jclass clazz, jobject object, const char *name) { + jfieldID field = env->GetFieldID(clazz, name, "Ljava/lang/String;"); + auto string = (jstring) env->GetObjectField(object, field); + return convertString(env, string); + } -std::string getStringField(JNIEnv *env, jobject object, const char *name) { - jclass clazz = env->GetObjectClass(object); - return getStringField(env, clazz, object, name); -} + std::string getStringField(JNIEnv *env, jobject object, const char *name) { + jclass clazz = env->GetObjectClass(object); + return getStringField(env, clazz, object, name); + } -class AndroidLogger final : public odr::Logger { -public: - static int to_android_log_level(odr::LogLevel level) { - switch (level) { - case odr::LogLevel::verbose: - return ANDROID_LOG_VERBOSE; - case odr::LogLevel::debug: - return ANDROID_LOG_DEBUG; - case odr::LogLevel::info: - return ANDROID_LOG_INFO; - case odr::LogLevel::warning: - return ANDROID_LOG_WARN; - case odr::LogLevel::error: - return ANDROID_LOG_ERROR; - case odr::LogLevel::fatal: - return ANDROID_LOG_FATAL; - default: - return ANDROID_LOG_UNKNOWN; + class AndroidLogger final : public odr::Logger { + public: + static int to_android_log_level(odr::LogLevel level) { + switch (level) { + case odr::LogLevel::verbose: + return ANDROID_LOG_VERBOSE; + case odr::LogLevel::debug: + return ANDROID_LOG_DEBUG; + case odr::LogLevel::info: + return ANDROID_LOG_INFO; + case odr::LogLevel::warning: + return ANDROID_LOG_WARN; + case odr::LogLevel::error: + return ANDROID_LOG_ERROR; + case odr::LogLevel::fatal: + return ANDROID_LOG_FATAL; + default: + return ANDROID_LOG_UNKNOWN; + } } - } - void flush() override {} + void flush() override {} - [[nodiscard]] bool will_log(odr::LogLevel level) const override { - return true; - } + [[nodiscard]] bool will_log(odr::LogLevel level) const override { + return true; + } -protected: - void log_impl(Time time, odr::LogLevel level, const std::string &message, - const std::source_location &location) override { - __android_log_print(to_android_log_level(level), "smn", "%s", message.c_str()); - } + protected: + void log_impl(Time time, odr::LogLevel level, const std::string &message, + const std::source_location &location) override { + __android_log_print(to_android_log_level(level), "smn", "%s", message.c_str()); + } -private: + private: -}; + }; } @@ -300,7 +300,7 @@ std::optional s_server; JNIEXPORT void JNICALL Java_at_tomtasche_reader_background_CoreWrapper_createServerNative(JNIEnv *env, jclass clazz, - jstring cachePath) { + jstring cachePath) { __android_log_print(ANDROID_LOG_INFO, "smn", "create server"); std::string cachePathCpp = convertString(env, cachePath); @@ -313,8 +313,9 @@ Java_at_tomtasche_reader_background_CoreWrapper_createServerNative(JNIEnv *env, } JNIEXPORT jobject JNICALL -Java_at_tomtasche_reader_background_CoreWrapper_hostFileNative(JNIEnv *env, jclass clazz, jstring prefix, - jobject options) { +Java_at_tomtasche_reader_background_CoreWrapper_hostFileNative(JNIEnv *env, jclass clazz, + jstring prefix, + jobject options) { __android_log_print(ANDROID_LOG_INFO, "smn", "host file"); auto logger = std::make_shared(); @@ -330,117 +331,129 @@ Java_at_tomtasche_reader_background_CoreWrapper_hostFileNative(JNIEnv *env, jcla return result; } - s_server->clear(); - - jclass optionsClass = env->GetObjectClass(options); + try { + s_server->clear(); - std::optional passwordCpp; - jfieldID passwordField = env->GetFieldID(optionsClass, "password", "Ljava/lang/String;"); - auto password = (jstring) env->GetObjectField(options, passwordField); - if (password != nullptr) { - passwordCpp = convertString(env, password); - } + jclass optionsClass = env->GetObjectClass(options); - jfieldID pagingField = env->GetFieldID(optionsClass, "paging", "Z"); - jboolean paging = env->GetBooleanField(options, pagingField); + std::optional passwordCpp; + jfieldID passwordField = env->GetFieldID(optionsClass, "password", "Ljava/lang/String;"); + auto password = (jstring) env->GetObjectField(options, passwordField); + if (password != nullptr) { + passwordCpp = convertString(env, password); + } - jfieldID editableField = env->GetFieldID(optionsClass, "editable", "Z"); - jboolean editable = env->GetBooleanField(options, editableField); + jfieldID pagingField = env->GetFieldID(optionsClass, "paging", "Z"); + jboolean paging = env->GetBooleanField(options, pagingField); - std::string outputPathCpp = getStringField(env, optionsClass, options, "outputPath"); + jfieldID editableField = env->GetFieldID(optionsClass, "editable", "Z"); + jboolean editable = env->GetBooleanField(options, editableField); - jclass listClass = env->FindClass("java/util/List"); - jmethodID addMethod = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z"); + std::string outputPathCpp = getStringField(env, optionsClass, options, "outputPath"); - jfieldID pageNamesField = env->GetFieldID(resultClass, "pageNames", "Ljava/util/List;"); - auto pageNames = (jobject) env->GetObjectField(result, pageNamesField); + jclass listClass = env->FindClass("java/util/List"); + jmethodID addMethod = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z"); - jfieldID pagePathsField = env->GetFieldID(resultClass, "pagePaths", "Ljava/util/List;"); - auto pagePaths = (jobject) env->GetObjectField(result, pagePathsField); + jfieldID pageNamesField = env->GetFieldID(resultClass, "pageNames", "Ljava/util/List;"); + auto pageNames = (jobject) env->GetObjectField(result, pageNamesField); - std::string inputPathCpp = getStringField(env, options, "inputPath"); - std::string prefixCpp = convertString(env, prefix); + jfieldID pagePathsField = env->GetFieldID(resultClass, "pagePaths", "Ljava/util/List;"); + auto pagePaths = (jobject) env->GetObjectField(result, pagePathsField); - odr::DecodePreference decodePreference; - decodePreference.engine_priority = {odr::DecoderEngine::poppler, odr::DecoderEngine::wvware, - odr::DecoderEngine::odr}; - odr::DecodedFile file = odr::open(inputPathCpp, decodePreference, *logger); + std::string inputPathCpp = getStringField(env, options, "inputPath"); + std::string prefixCpp = convertString(env, prefix); - if (file.password_encrypted()) { - if (!passwordCpp.has_value()) { - env->SetIntField(result, errorField, -2); - return result; - } try { - file = file.decrypt(passwordCpp.value()); - } catch (...) { - env->SetIntField(result, errorField, -2); - return result; - } - } - - if (file.is_document_file()) { - // TODO this will cause a second load - s_document = file.as_document_file().document(); - } + odr::DecodePreference decodePreference; + decodePreference.engine_priority = {odr::DecoderEngine::poppler, + odr::DecoderEngine::wvware, + odr::DecoderEngine::odr}; + odr::DecodedFile file = odr::open(inputPathCpp, decodePreference, *logger); - odr::HtmlConfig htmlConfig; - htmlConfig.embed_images = false; - htmlConfig.embed_shipped_resources = true; - htmlConfig.relative_resource_paths = false; - htmlConfig.text_document_margin = paging; - htmlConfig.editable = editable; + if (file.password_encrypted()) { + if (!passwordCpp.has_value()) { + env->SetIntField(result, errorField, -2); + return result; + } + try { + file = file.decrypt(passwordCpp.value()); + } catch (...) { + env->SetIntField(result, errorField, -2); + return result; + } + } - try { - std::string output_tmp = outputPathCpp + "/tmp"; - std::filesystem::create_directories(output_tmp); - odr::HtmlService service = odr::html::translate(file, output_tmp, htmlConfig, logger); - s_server->connect_service(service, prefixCpp); - odr::HtmlViews htmlViews = service.list_views(); - - for (const auto &view: htmlViews) { - __android_log_print(ANDROID_LOG_INFO, "smn", "view name=%s path=%s", view.name().c_str(), view.path().c_str()); - if (file.is_document_file() && ( - (((file.as_document_file().document_type() == odr::DocumentType::presentation) || - (file.as_document_file().document_type() == odr::DocumentType::drawing)) && - (view.name() != "document")) || - ((file.as_document_file().document_type() == odr::DocumentType::spreadsheet) && - (view.name() == "document")))) { - continue; + if (file.is_document_file()) { + // TODO this will cause a second load + s_document = file.as_document_file().document(); } - jstring pageName = env->NewStringUTF(view.name().c_str()); - env->CallBooleanMethod(pageNames, addMethod, pageName); + odr::HtmlConfig htmlConfig; + htmlConfig.embed_images = false; + htmlConfig.embed_shipped_resources = true; + htmlConfig.relative_resource_paths = false; + htmlConfig.text_document_margin = paging; + htmlConfig.editable = editable; + + std::string output_tmp = outputPathCpp + "/tmp"; + std::filesystem::create_directories(output_tmp); + odr::HtmlService service = odr::html::translate(file, output_tmp, htmlConfig, logger); + s_server->connect_service(service, prefixCpp); + odr::HtmlViews htmlViews = service.list_views(); + + for (const auto &view: htmlViews) { + __android_log_print(ANDROID_LOG_INFO, "smn", "view name=%s path=%s", + view.name().c_str(), view.path().c_str()); + if (file.is_document_file() && ( + (((file.as_document_file().document_type() == + odr::DocumentType::presentation) || + (file.as_document_file().document_type() == + odr::DocumentType::drawing)) && + (view.name() != "document")) || + ((file.as_document_file().document_type() == + odr::DocumentType::spreadsheet) && + (view.name() == "document")))) { + continue; + } + + jstring pageName = env->NewStringUTF(view.name().c_str()); + env->CallBooleanMethod(pageNames, addMethod, pageName); - std::string pagePathCpp = - "http://localhost:29665/file/" + prefixCpp + "/" + view.path(); - jstring pagePath = env->NewStringUTF(pagePathCpp.c_str()); - env->CallBooleanMethod(pagePaths, addMethod, pagePath); + std::string pagePathCpp = + "http://localhost:29665/file/" + prefixCpp + "/" + view.path(); + jstring pagePath = env->NewStringUTF(pagePathCpp.c_str()); + env->CallBooleanMethod(pagePaths, addMethod, pagePath); + } + } catch (const odr::UnknownFileType &e) { + __android_log_print(ANDROID_LOG_ERROR, "smn", "Unknown file type: %s", e.what()); + env->SetIntField(result, errorField, -5); + return result; + } catch (const odr::UnsupportedFileType &e) { + __android_log_print(ANDROID_LOG_ERROR, "smn", "Unsupported file type: %s", e.what()); + env->SetIntField(result, errorField, -5); + return result; + } catch (const std::exception &e) { + __android_log_print(ANDROID_LOG_ERROR, "smn", "Unhandled C++ exception: %s", e.what()); + env->SetIntField(result, errorField, -4); + return result; + } catch (...) { + __android_log_print(ANDROID_LOG_ERROR, "smn", + "Unhandled C++ exception without further information"); + env->SetIntField(result, errorField, -4); + return result; } - } catch (const std::exception &e) { - __android_log_print(ANDROID_LOG_ERROR, "smn", "Unhandled C++ exception: %s", e.what()); - env->SetIntField(result, errorField, -1); - return result; - } catch (const std::string &s) { - __android_log_print(ANDROID_LOG_ERROR, "smn", "Unhandled C++ string exception: %s", s.c_str()); - env->SetIntField(result, errorField, -1); - return result; - } catch (int i) { - __android_log_print(ANDROID_LOG_ERROR, "smn", "Unhandled C++ int exception: %i", i); - env->SetIntField(result, errorField, -1); - return result; } catch (...) { - __android_log_print(ANDROID_LOG_ERROR, "smn", - "Unhandled C++ exception without further information"); - env->SetIntField(result, errorField, -1); + env->SetIntField(result, errorField, -3); return result; } + env->SetIntField(result, errorField, 0); return result; } JNIEXPORT void JNICALL -Java_at_tomtasche_reader_background_CoreWrapper_listenServerNative(JNIEnv *env, jclass clazz, jint port) { +Java_at_tomtasche_reader_background_CoreWrapper_listenServerNative(JNIEnv *env, jclass clazz, + jint port) { __android_log_print(ANDROID_LOG_INFO, "smn", "listen ..."); s_server->listen("127.0.0.1", port);